¿Puedo utilizar mv file1 file2
de una manera que sólo se mueve file1
a file2
si file2
que no existe?
He intentado
yes n | mv -i file1 file2
(esto permite mv
preguntar si el archivo 2 debe ser anulado y automáticamente responde que no), pero además de abusar -i
, tampoco me da buenos códigos de error (siempre 141 en lugar de 0 si se movió y algo más si no se movió)
pipefail
opción activada ya que 141 sería el estado de salida deyes
,mv
que no tendría ninguna razón para obtener un SIGPIPE aquí.-T
para eso.mv
lugar del deyes
, la solución más simple podría sermv -i file1 file2 < <(yes n)
Respuestas:
mv -vn file1 file2
. Este comando hará lo que quieras. Puedes saltarte-v
si quieres.-v
lo hace detallado: mv le dirá que movió el archivo si lo mueve (útil, ya que existe la posibilidad de que el archivo no se mueva)-n
se mueve solo si el archivo2 no existe.Sin embargo, tenga en cuenta que esto no es POSIX como lo menciona ThomasDickey .
fuente
strace
muestra que usa (en mi sistema): stat ("file2", 0x7ffe3e705d10) = -1 ENOENT (No existe tal archivo o directorio) lstat ("file1", {st_mode = S_IFREG | 0644, st_size = 0, ...}) = 0 lstat ("file2", 0x7ffe3e705a10) = -1 ENOENT (No existe tal archivo o directorio) renombrar ("file1", "file2") = 0 lseek (0, 0, SEEK_CUR) = -1 ESPIPE (búsqueda ilegal). Así que parece que se usa cambiar el nombre. La solución @ StéphaneChazelas parece ser la correcta si realmente quieres hacerlo sin carreras.renameat2
mv -n
Desde
man mv
un sistema GNU:En un sistema FreeBSD:
fuente
O:
Solo se ejecutaría
mv
sifile2
no existe. Tenga en cuenta que no garantiza quefile2
no se anule a porque sefile2
podría haber creado entre la prueba y la pruebamv
, pero tenga en cuenta que al menos las versiones actuales de GNUmv
con-i
o-n
no dan esa garantía tampoco (aunque la condición de carrera es más estrecha allí ya que la verificación se realiza dentromv
).Por otro lado, es portátil, le permite discriminar entre los casos y funciona independientemente del tipo de
file2
archivo (regular, canalización, incluso directorio ).fuente
renameat2
que le puedes dar unaRENAME_NOREPLACE
bandera. Creo que esto comprueba atómicamente la existencia del archivo y luego lo mueve.Un enfoque libre de raza con GNU
ln
proporcionadofile1
no es del tipo directorio :(Excepto por errores en algunos sistemas de archivos de red), eso garantiza que ningún
file2
archivo se anulará (o que sifile2
es de tipo directorio,file1
no se moverá a él), porque lalink()
llamada al sistema, al contrario de larename()
llamada del sistema, fallará si el El objetivo existe.Sin embargo, habrá un estado intermedio donde el archivo existe tanto como
file1
yfile2
.La
-T
opción (hacer siempre un directoriolink("file1", "file2")
aunquefile2
sea de tipo) es específico de GNU.También puedes usar el
link
comando:Sin embargo, si
file1
es un enlace simbólico, dependiendo de la implementación,file2
será un enlace directo a ese enlace simbólico o al destino de ese enlace simbólico (en Solaris, use/usr/sbin/link
, not/usr/xpg4/bin/link
).fuente
renameat2
con banderaRENAME_NOREPLACE
es atómica?También puede usar
test -e name
que devolverá verdadero si el nombre existe (independientemente del archivo, directorio o enlace simbólico).Por ejemplo:
fuente
ln -s doesnotexist exists; test -e exists || echo "does it really not exist?"
. Lo mismo con por ejemploln -s /var/spool/cron/crontabs/. exists
(y no eres root ni miembro del grupo crontab).