Primero haré una base de prueba: 5 archivos y una carpeta:
touch file1 file2 file3 file4 file5
mkdir folder
A continuación, ejecutaré un comando de prueba. La -v
opción especifica que quiero que se impriman todos los comandos que ejecuta el shell stderr
. La -x
opción especifica que quiero que se imprima lo mismo stderr
, pero quiero que se haga después de evaluar el comando pero antes de que el shell lo ejecute.
sh -cxv 'echo mv *'
SALIDA
echo mv *
+ echo mv file1 file2 file3 file4 file5 folder
mv file1 file2 file3 file4 file5 folder
Entonces verá que el comando que le doy al shell es echo mv *
y el comando que ejecuta el shell después de *
expandirse es echo mv
seguido por todos esos archivos y la carpeta.
Por defecto, el shell expandirá globos como:
sh -cxv 'echo file[1-5]'
SALIDA
echo file[1-5]
+ echo file1 file2 file3 file4 file5
file1 file2 file3 file4 file5
Este es el resultado de la set [+-]f
función glob:
sh -cxvf 'echo file[1-5]'
SALIDA
echo file[1-5]
+ echo 'file[1-5]'
file[1-5]
Entonces, cuando ejecuta un comando en un shell configurado con opciones predeterminadas como mv *
el shell, se expande en la *
palabra una lista de argumentos de todos los archivos en el directorio actual ordenados de acuerdo con la configuración regional. Realiza la llamada exec(ve)
al sistema mv
(esencialmente) con esta lista de argumentos adjunta. Entonces mv
obtiene todos los argumentos a medida que el shell los engloba y los ordena. Además strace
de ver estos efectos, puede usar la depuración nuevamente como:
sh -s -- mv * <<\SCRIPT
sed -n l /proc/$$/cmdline
echo "$@"
SCRIPT
SALIDA
sh\000-s\000--\000mv\000file1\000file2\000file3\000file4\000file5\000folder\
\000$
mv file1 file2 file3 file4 file5 folder
Y portablemente:
( PS4= IFS=/; set -x mv *; : "/$*/" ) 2>&1
SALIDA
: /mv/file1/file2/file3/file4/file5/folder/
Básicamente, el shell se ejecuta mv
con el contenido del directorio (si no está vacío y no incluye archivos / carpetas con nombres que comienzan con .
) como su lista de argumentos. mv
es POSIX especifica para interpretar su argumento final como un directorio si se invoca con más de dos argumentos - de la misma manera ln
es (porque, de hecho, son herramientas muy similares en función subyacente) .
Sin echo
embargo, es suficiente :
sh -cxv 'mv *' ; ls
SALIDA
mv *
+ mv file1 file2 file3 file4 file5 folder
folder/
Todos los archivos se movieron al argumento final, porque es una carpeta. ¿Y si no es una carpeta?
sh -cxv 'cd *; mv *'; ls . *
SALIDA
cd *; mv *
+ cd folder
+ mv file1 file2 file3 file4 file5
mv: target ‘file5’ is not a directory
.:
folder/
folder:
file1 file2 file3 file4 file5
Así es como debería comportarse POSIX especifica mv
en ese caso:
mv [-if] source_file target_file
mv [-if] source_file... target_dir
En la primera forma de sinopsis, la mv
utilidad moverá el archivo nombrado por el operando source_file al destino especificado por el archivo target_file . Esta primera forma de sinopsis se supone cuando el operando final no nombra un directorio existente y no es un enlace simbólico que se refiere a un directorio existente. En este caso, si el archivo de origen nombra un archivo que no es de directorio y el archivo de destino finaliza con un /slash
carácter final , mv
se tratará como un error y no se procesarán operandos de archivo de origen .
En la segunda forma de sinopsis, mv
moverá cada archivo nombrado por un operando de archivo de origen a un archivo de destino en el directorio existente nombrado por el operando target_dir , o referenciado si target_dir es un enlace simbólico que se refiere a un directorio existente. La ruta de destino para cada archivo de origen será la concatenación del directorio de destino, un solo /slash
carácter si el destino no terminó en a /slash
, y el último componente de nombre de ruta del archivo de origen . Se asume esta segunda forma cuando el operando final nombra un directorio existente.
Entonces, si se *
expande a: