¿Cómo evitar que 'mv' mueva una colección de archivos a una única normal?

17

Acabo de perder una pequeña parte de mi colección de audio, por un estúpido error que cometí. :-(
DIVERTIDO Tenía una copia de seguridad bastante reciente, pero aún así era irritante. Aparte de la suya, el otro culpable que hizo la travesura fue mv, que se mostrará de la siguiente manera:

Los archivos de audio tenían un cierto esquema:

ARTIST - Some Title YY.mp3

donde YYes la especificación año de 2 dígitos.

mkdir 90<invisible control character>

(¡Hasta este momento, no sabía que había escrito un tercer carácter en exceso que era invisible ...!)
En lugar de tener todo en un directorio, quería tener toda la música de los 90 en un solo directorio. Entonces escribí:

find . -name '* 9?.mp3' -exec mv {} 90 \;

No es tan difícil de conseguir la idea de lo que sucedió eh? : ->
El (desastroso) resultado fue una virgen vacío directorio llamado '90 algo '(con algo de ser el carácter de control 'invisible') y un solo archivo llamado '90', sobrescritos n veces.

TODOS LOS ARCHIVOS habían desaparecido. : - (((obviamente)

Wish mvhubiera verificado a tiempo si la firma del "archivo" de destino (recuerde en * NIX: Everything Is A File ) comienza con un d------(por ejemplo drwxr-xr-x). Y, por supuesto, si el destino existe en absoluto. Hay una variante del escenario antes mencionado, en las que simplemente se olvidó de mkdirla guía en primer lugar. (pero por supuesto, asumiste que está ahí ...)

Incluso nuestra mascota OS-odio empezando por la capital W hace este. Incluso se le solicita que especifique el tipo de destino (¿archivo? ¿Directorio?) Si lo solicita.

Por lo tanto, me pregunto si nosotros * NIXers todavía tenemos que escribirnos un " mvscriptlet" solo para evitar este tipo de sorpresas no deseadas.

error de sintaxis
fuente
2
No se habían ido todos los archivos. Al menos uno .mp3debería estar allí con el nombre 90, podría haber sido uno para el que no tenía una copia de seguridad.
Anthon
2
Je, usted tiene un cínico sentido del humor, que le nuez! :-P Bueno, que era el archivo llamado "un solo archivo" en negrita en mi OP. :)
syntaxerror
2
mvno es el problema aquí, técnicamente, no sabe que usted está en movimiento una serie de archivos. Está ejecutando mvuna vez para cada archivo. Así es como find -exec ;funciona. Si lo hubiera usado find -exec +(como en algunos de los comentarios) mv habría gritado tan pronto como recibiera más de un argumento.
Etan Reisner el
Aunque la ejecución mvde cada archivo puede parecer un poco menos pensada al principio, será (como dije anteriormente) la única solución sensata una vez que los archivos de origen estén dispersos entre varios subdirectorios. Que en mi caso de prueba, los archivos de origen estaban todos en un directorio no significa que sea mi caso de prueba real . Es, de hecho, sólo una simplificación, porque puede elaborar fácilmente en que por mi cuenta más adelante. Además, se hace preguntas menos tiempo para leer debido a su longitud reducida. :)
syntaxerror
¿Por qué esperarías mvrequerir que exista el destino? mv oldfile newfilees la forma de cambiar el nombre de un archivo, y es una tontería esperar newfileque ya exista y sea un directorio.
Barmar

Respuestas:

37

Se puede añadir una /al destino si desea mover los archivos a un directorio. En caso de que el directorio no exista, recibirá un error:

mv somefile somedir/
mv: cannot move ‘somefile’ to ‘somedir/’: Not a directory

En caso de que el directorio exista, mueve el archivo a ese directorio.

Marco
fuente
44
Muchas gracias! Ese debería ser mi futuro salvavidas entonces. Te debo una. (Así como una nota, un amigo mío había hecho el mismo error hace unos años, así que siento que no estoy solo.)
SyntaxError
1
Además, al usar ciertos shells, tiene la opción Tab para completar automáticamente los nombres de archivo por usted. Si no puedo recordar el nombre del directorio, y no quiero un desastre como el que has tenido recientemente, simplemente introduce uno o dos caracteres del nombre del directorio y HIT TAB . Entonces puede estar seguro de que existe, porque el autocompletado lo puso allí ...
Andyz Smith
@AndyzSmith Bueno, eso es todo. Usted puede llamar un hábito mío utilizar solamente siempre TAB para los complicados directorios o caminos, pero no para los de tipo 2 carta. :) Pero ahora que lo pienso ... quizás realmente debería considerar este último caso, así de ahora en adelante.
SyntaxError
20

GNU coreutils mvya tiene una opción que especifica que desea mover a un directorio: -t/ --target-directory. Si el argumento de esa opción no existe, mvse quejará en lugar de mover todos sus archivos al mismo nombre de archivo.

Yo habría escrito su motor de la siguiente manera:

find . -name '* 9?.mp3' -exec mv -t 90 {} +

Tenga en cuenta el uso de, en +lugar de \;, agrupar tantos nombres de archivos como sea posible, lo que resulta en una ejecución más rápida.

Anthon
fuente
Gracias. (Esperando que no sea uno de esos GNU-isms de nuevo, sin embargo.)
syntaxerror
2
@error de sintaxis. Es un GNUismo. POSIXY:find . -name '* 9?.mp3' -exec sh -c 'exec mv "$@" 90/' sh {} +
Stéphane Chazelas
¡Muchas gracias por este juego de palabras más SNEAKY! Solo piensa que fue un +5 que te di. :) Hará innecesarios muchos intentos de prueba y error .--- Y sabes muy bien por qué hice ese comentario. Solo necesito estar en una máquina que sea directamente POSIX (no ocurre muy raramente), y voy a tener el siguiente problema allí mismo. :)
syntaxerror
1
@syntaxerror Si esta respuesta resuelve su problema, tómese un minuto y haga clic en la marca de verificación debajo del recuento de votos a la izquierda, esto significará para todos que su problema se ha resuelto y es la forma en que se expresan las gracias en el sitio. Vi que solo algunas de sus otras preguntas respondidas han aceptado respuestas, es posible que desee revelarlas también.
Anthon
No, es simplemente un propósito. Frecuentemente esperaba varias semanas o, a veces, 2 meses hasta que aceptara una respuesta. La razón es que algunas personas muy informadas tienen un horario MUY cargado y solo pueden encontrar tiempo para dar su (generalmente mejor) respuesta después de un par de semanas. Así que siempre me parece respetuoso esperar a que pasen por allí. Bueno, y si realmente no lo hacen, no dudaré en hacer clic en la marca de verificación, seguro. Además, no veo por qué algunas personas siempre tienen tanta prisa por SE + sus sabores. Fácil lo hace, amigos. No saltes el arma. :) Este no es tu jefe presionándote.
syntaxerror
10

Además, si generalmente planea evitar sobrescrituras accidentales en el futuro, existe la -iopción mv. Personalmente no puedo pensar en ningún inconveniente si usted

alias mv='mv -i'

Si necesita sobrescribir algo, simplemente pase la -fopción.

Los alias solo surten efecto si escribe el comando directamente en un shell interactivo, no para casos como invocación por find. Podrías haber corrido

find . -name '* 9?.mp3' -exec mv -i {} 90 \;

y luego se le habría preguntado si mvhubiera intentado sobrescribir un archivo existente.

ayekat
fuente
44
O, como probablemente no sabía, escriba ´ \ mv `en lugar de mv. Este "truco" menos conocido hará que el comando con la barra diagonal inversa antepuesta ignore cualquier definición de alias.
syntaxerror
Por supuesto, si realmente estás hablando find ... -exec mv ..., entonces necesitarás crear ~/bin/mv(o algún otro directorio apropiado) y hacerlo /bin/mv -i "$@", porque find ... -execno mira los alias.
G-Man dice 'Restablece a Monica' el
En este caso, preferiría env mv. Menos mecanografía. :) Como mi diseño de teclado local requiere que se presione la tecla MAYÚS para una barra diagonal, siempre preferiría las versiones sin barra (si corresponde).
syntaxerror
1
@syntaxerror: Por cierto, cuando responde un comentario (en un nuevo comentario), es convencional mencionar el nombre del autor, precedido por "@", como en "@ G-Man". De esa manera me notifican. (Yo era capaz de responder a su último comentario de una manera semi-oportuna porque me notificó por Jenny D's comentario.) Puede abreviar, o utilizar un nombre completo (sin espacios), por ejemplo, “@ StéphaneChazelas”. El autor de una publicación es notificado automáticamente de los comentarios a esa publicación. Consulte la respuesta en los párrafos de comentarios de esta página de ayuda .
G-Man dice 'reinstalar a Monica' el
1
Lo siento, estuve sin internet por un día. @ G-Man Estoy de acuerdo en que tener una versión personal mven una ubicación al principio $PATHsería una solución más limpia. Por otro lado me primarly la casualidad de que mvpor descuido en el intérprete de comandos interactivo (porque pasa rápido). En el momento en que compongo algo más complejo, como findun forbucle o incluso un script de shell, tiendo a realizar algunas ejecuciones en seco (usando echo) para asegurarme de no romper algo. En aquellos casos en que no necesito una mano de retención mv, porque ya estoy poniendo algunos pensamientos en ella.
ayekat
7

Además de las excelentes respuestas anteriores, me gustaría aclarar por qué no recibió la pregunta sobre si se debe mover los archivos o no.

Si se mueve un archivo a un nuevo nombre, y ese nombre no es un directorio, mvse cambie el nombre del archivo con el nuevo nombre.

El problema aquí es que estaba utilizando findpara ejecutar mvuna vez por archivo , no una vez para todos los archivos .

Si, en cambio, se había hecho mv *90.mp3 90, entonces mvhabría fallado con el mensaje de error que "el archivo de destino no es un directorio".

Otro consejo es utilizar la implementación del tabulador al escribir la ruta de destino. Se le mostrará si el destino es un directorio añadiendo /al nombre de destino. También puede utilizar mv -ique se le pregunte si desea sobrescribir un archivo existente.

Jenny D
fuente
Si, en cambio, lo hubiera hecho mv *90.mp3 90, mv habría fallado con el mensaje de error de que "el archivo de destino no es un directorio". Ja, sí, ¿por qué tan complicado eh? Uso tu línea y seré feliz. Sin embargo, solo en este caso trivial. :) Porque esta es la forma normal en que hago mis preguntas: las reduciría por simplicidad. Nadie se ha opuesto findhasta ahora considerando que los archivos de los 90 también podrían estar dispersos en varios subdirectorios que también quiero "atrapar". Si y solo si siempre están en un directorio de origen, su mvlínea es aplicable.
syntaxerror
@syntaxerror Muy cierto: quise decir esto como un ejemplo de cómo se mvcomporta, no como una crítica de su elección de herramientas.
Jenny D
1
Probablemente se podría construir algo combinar findy mv, por ejemplo find /music -type d -exec mv {}/*90.mp3 targetdir\;- pero ahora me siento un poco como estoy complicar excesivamente, y el simple uso -io -tes más eficiente
Jenny D
1
@ Jenny: Si su find . -type d -exec mv {}/*9?.mp3 target \;ejemplo funcionó, todavía existiría el riesgo de que el mvcomando se vea mv file targetpara cada directorio que contenga solo un *9?.mp3archivo; por lo que todos esos archivos (excepto el último) se perderían.
G-Man dice 'Restablecer a Monica' el
44
@syntaxerror: aplaudo su esfuerzo por exponer la parte esencial de su problema, en lugar de la secuencia de comandos completa de 42,000 líneas en la que ocurre. Pero, incluso si se quería hacer algo para todos los *.mp3archivos en un árbol de directorios, se podía shopt -s globstary ejecute el comando en **/*.mp3- el **actuará como una find.
G-Man dice 'Restablecer a Mónica' el
1

Como una estrategia alternativa de uso general, me gustaría sugerir convertir este tipo de operación en un script temporal. Prefiero mirar los resultados findy convertirlos en un mvcomando a mano, asegurando que entiendo lo que estoy haciendo antes de ejecutar. p.ej.

find . -name '* 9?.mp3' > tmp
vim tmp

Ahora puedo mirar a través de una lista de nombres de archivo y volver a escribir el contenido del archivo como un comando shell.

  • Ponga el contenido del archivo en una línea: ggVGJ
  • Anteponer: Imv [esc]
  • Adjuntar: Asomedir/ [esc]
  • Guarda el archivo. Leelo de nuevo. Tomar un respiro.
  • Ejecutar source tmpen la línea de comando.

Es una estrategia conservadora, pero he sido mordido con demasiada frecuencia por comandos mal escritos -execo sedexpansiones de shell mal entendidas, y prefiero adoptar un enfoque lento y consistente.

En otras palabras: soy demasiado cobarde para usar -exec.

Tom Rees
fuente
1
Omitió el :%s/.*/"&"/paso, porque, en este caso, sabe que cada nombre de archivo contiene al menos un espacio.
G-Man dice 'Restablecer a Monica' el
1
Esto lo morderá si los nombres de los archivos contienen espacios u otros caracteres especiales. Es necesario citar de manera adecuada. Revisar una lista de comandos no es particularmente probable que detecte errores. Hay formas mucho mejores de revisar los comandos antes de ejecutarlos, como ejecutar en echo mvlugar de ejecutarlos mvy luego eliminarlos echosi está satisfecho.
Gilles 'SO- deja de ser malvado'
Detectar un error como nombres de archivos con espacios es precisamente con lo que ayudará esta técnica :-)
Tom Rees
0

Otra opción:

-n, --no-clobber no sobrescribir un archivo existente

es lo mismo que -i, pero no preguntará, fallará.

Jorge Nerín
fuente