mover archivos que coinciden con un patrón que captura el directorio

mover archivos que coinciden con un patrón que captura el directorio

Considerar:

$ ls
foo  xyfooz.tex
$ find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;
$ ls ./foo*
xyfooz.tex

Estoy tratando de lograr la misma secuencia de instrucciones solo con mv, no encontrar:

$ ls
foo  xyfooz.tex
$ mv *foo* foo
mv: cannot stat '*foo*': No such file or directory
$ ls ./foo*
xyfooz.tex

Además (editar),

$ ls -F
foo/  xyfooz.tex*
$ mv -v *foo* foo

mv: cannot move 'foo' to a subdirectory of itself, 'foo/foo'
'xyfooz.tex' -> 'foo/xyfooz.tex'

Cómo asegurarse de que no aparezca la advertencia de 'estadísticas'. En otras palabras, supongo, ¿refinaría el patrón para limitar el origen a archivos, no a directorios?

GNU bash, versión 4.3.48(1) (x86_64-pc-linux-gnu)

$ cat /etc/os-release
NAME="Linux Mint"
VERSION="18.3 (Sylvia)"

Respuesta1

Conmv

cannot move 'foo' to a subdirectory of itselfes inofensivo en este caso, puedes ignorarlo. Quiero decir, mvmoverá el resto a pesar de la advertencia. Sin embargo, el estado de salida no será 0, esto puede ser un gran obstáculo en scripts en los que desea abortar o tomar una acción correctiva si mvno tiene éxito.

Esto ya ha sidodijo en un comentario: puedes silenciar mvredirigiendo stderr con 2>/dev/null; Sin embargo, también se redireccionarán otras advertencias o errores.

Creo que no se puede "refinar fácilmente el patrón para limitar la fuente a archivos, no a directorios". Aún así, puedes adoptar un enfoque que no coincida con el literal foo. El siguiente enfoque no tiene nada que ver con archivos o directorios; sólo con nombres, porque los globos tratan con nombres; por lo que en algunos casos puede no ser suficiente.

El *foo*globo coincide con cuatro tipos disyuntivos de objetos:

  1. foosí mismo
  2. foo solo con prefijo, como bar-foo: puedes unirlos por*?foo
  3. foo con prefijo y sufijo, como bar-foo-baz*?foo?*
  4. foo solo con postfix, como foo-bazfoo?*

Para excluir foosolo necesita los últimos tres (2-4), por lo que el primer enfoque puede ser:

mv *?foo *?foo?* foo?* foo/

A menos que tenga nullglobconfigurada la opción Shell, cualquier patrón debe coincidir con algo; de lo contrario, se pasará mvliteralmente. No quieres esto. La solución es ejecutar el siguiente comando de antemano:

shopt -s nullglob

Esta opción de shell hará que cualquier globo sin igual se expanda hasta quedar en nada. Te aconsejo que investigues dotglobla opción también.

Tenga en cuenta que *?foo*coincide (2-3). Partidos similares *foo?*(3-4). Además, considere --indicar mvque todos los argumentos siguientes no son opciones. De esta manera, un archivo como --foono se interpretará como una opción (desconocida). Esto lleva a dos comandos mejores:

mv -- *?foo* foo?* foo/

o

mv -- *?foo *foo?* foo/

No importa cuál elijas. Simplemente no lo uses mv *?foo* *foo?* foo/; pasará (por ejemplo) bar-foo-baza mvdos veces (es decir, como dos argumentos separados), generando así un error cuando la herramienta intente mover este objeto por segunda vez.

Cuando no se encuentra ninguna coincidencia, mis mvcomandos degenerarán mv foo/y arrojarán un error. Su mvcomando original (sin nullglobconfigurar) obtendría el literal *foo*y arrojaría otro error.


Sesión de consola de ejemplo:

$ shopt -s nullglob
$ mkdir foo
$ touch bar-foo bar-foo-baz foo-baz xyfooz.tex ./--foo
$ mv -v -- *?foo* foo?* foo/
'bar-foo' -> 'foo/bar-foo'
'bar-foo-baz' -> 'foo/bar-foo-baz'
'--foo' -> 'foo/--foo'
'xyfooz.tex' -> 'foo/xyfooz.tex'
'foo-baz' -> 'foo/foo-baz'
$ 

truco sencillo

Digamos dummyque no existe.

mv foo dummy
mv *foo* dummy/
mv dummy foo

Cambiar el nombre de un directorio debería ser muy rápido en sistemas de archivos basados ​​en inodos. Los programas que tienen archivos internos foo/ya abiertos no deberían interferir ni romperse porque lo que importa es el inodo. Sin embargo, si algún programa necesita abrir un archivo (por su ruta, incluido foo/) entre el primero y el tercero mv, fallará. Pueden ocurrir otros efectos secundarios (imagínese un programa de terceros que se recrea foo/mientras tanto), es por eso que llamo a este enfoque un truco. Piénselo dos veces antes de usarlo.


con findtodos modos

Distinguir archivos de directorios es un trabajo para find. Lo que hiciste con findes básicamente la forma correcta. Lo que quieres hacer (y lo que hice arriba) con mvun shell global parece... bueno, "menos correcto" como mucho. Este es tu comando:

find . -maxdepth 1 -type f -name '*foo*' -exec mv {} foo \;

Esto findejecutará un archivo separado mvpara cada archivo coincidente, todo el comando funcionará mal. Por este motivo, es posible que desee cambiar a un solo archivo mv. Si esta es tu línea de pensamiento (y mveres findlo suficientemente rico), entonces deberías considerar esta forma "muy correcta":

find . -maxdepth 1 -type f -name '*foo*' -exec mv -t foo/ -- {} +

Las ventajas sobre su findcomando y/o sobre simple mvcon globo(s):

  • uno mvpuede tener varios archivos con los que trabajar;
  • aún así, si hay demasiados archivos para que los maneje una línea de comando, findejecutará mvprocesos adicionales;
  • en caso de que ningún archivo coincida con el patrón, mvno se ejecutará en absoluto;
  • gracias a --un archivo como --foono se interpretará como una opción (desconocida) mv;

información relacionada