Primeiro, farei uma base de teste - 5 arquivos e uma pasta:
touch file1 file2 file3 file4 file5
mkdir folder
Em seguida, vou executar um comando de teste. A opção -v
especifica que eu quero que todos os comandos que o shell executa sejam impressos em stderr
. A opção -x
especifica que eu quero o mesmo impresso em stderr
- mas Eu quero que seja feito após o comando ser avaliado mas antes o shell o executa.
sh -cxv 'echo mv *'
OUTPUT
echo mv *
+ echo mv file1 file2 file3 file4 file5 folder
mv file1 file2 file3 file4 file5 folder
Então você vê que o comando que eu alimento o shell é echo mv *
e o comando que o shell executa depois *
é expandido é echo mv
seguido por todos esses arquivos e a pasta.
Por padrão, o shell expandirá globs como:
sh -cxv 'echo file[1-5]'
OUTPUT
echo file[1-5]
+ echo file1 file2 file3 file4 file5
file1 file2 file3 file4 file5
Este é um resultado da função set [+-]f
glob:
sh -cxvf 'echo file[1-5]'
OUTPUT
echo file[1-5]
+ echo 'file[1-5]'
file[1-5]
Portanto, quando você executa um comando em um shell configurado com opções padrão como mv *
, o shell expande para a lista% de argumentos de *
de todos os arquivos no diretório atual classificados de acordo com o código do idioma. Ele faz o syscall exec(ve)
para mv
(essencialmente) com esta lista de argumentos anexada. Portanto, mv
obtém todos os argumentos à medida que o shell os globaliza e os classifica. Além de fazer strace
para ver esses efeitos, você pode usar a depuração novamente como:
sh -s -- mv * <<\SCRIPT
sed -n l /proc/$$/cmdline
echo "$@"
SCRIPT
OUTPUT
sh( PS4= IFS=/; set -x mv *; : "/$*/" ) 2>&1
0-s: /mv/file1/file2/file3/file4/file5/folder/
0--sh -cxv 'mv *' ; ls
0mvmv *
+ mv file1 file2 file3 file4 file5 folder
folder/
0file1sh -cxv 'cd *; mv *'; ls . *
0file2cd *; mv *
+ cd folder
+ mv file1 file2 file3 file4 file5
mv: target ‘file5’ is not a directory
.:
folder/
folder:
file1 file2 file3 file4 file5
0file3mv [-if] source_file target_file
mv [-if] source_file... target_dir
0file4touch file1 file2 file3 file4 file5
mkdir folder
0file5sh -cxv 'echo mv *'
0folder\
echo mv *
+ echo mv file1 file2 file3 file4 file5 folder
mv file1 file2 file3 file4 file5 folder
0$
mv file1 file2 file3 file4 file5 folder
E portably:
sh -cxv 'echo file[1-5]'
OUTPUT
echo file[1-5]
+ echo file1 file2 file3 file4 file5
file1 file2 file3 file4 file5
Basicamente, o shell executa mv
com o conteúdo do diretório (se não estiver vazio e não incluindo arquivos / pastas com nomes começando com .
) como sua lista de argumentos. mv
é POSIX especificado para interpretar seu argumento final como um diretório se ele é chamado com mais de dois argumentos - da mesma forma que ln
é (porque, na verdade, são ferramentas incrivelmente semelhantes na função subjacente ) .
Suficiente echo
es embora:
sh -cxvf 'echo file[1-5]'
OUTPUT
echo file[1-5]
+ echo 'file[1-5]'
file[1-5]
Todos os arquivos foram movidos para o argumento final - porque é uma pasta. Agora, e se não for uma pasta?
sh -s -- mv * <<\SCRIPT
sed -n l /proc/$$/cmdline
echo "$@"
SCRIPT
OUTPUT
sh( PS4= IFS=/; set -x mv *; : "/$*/" ) 2>&1
0-s: /mv/file1/file2/file3/file4/file5/folder/
0--sh -cxv 'mv *' ; ls
0mvmv *
+ mv file1 file2 file3 file4 file5 folder
folder/
0file1sh -cxv 'cd *; mv *'; ls . *
0file2cd *; mv *
+ cd folder
+ mv file1 file2 file3 file4 file5
mv: target ‘file5’ is not a directory
.:
folder/
folder:
file1 file2 file3 file4 file5
0file3mv [-if] source_file target_file
mv [-if] source_file... target_dir
0file4%pre%0file5%pre%0folder\
%pre%0$
mv file1 file2 file3 file4 file5 folder
É assim que o POSIX especifica mv
deve se comportar nesse caso:
%pre%
In the first synopsis form, the mv
utility shall move the file named by the source_file operand to the destination specified by the target_file. This first synopsis form is assumed when the final operand does not name an existing directory and is not a symbolic link referring to an existing directory. In this case, if source_file names a non-directory file and target_file ends with a trailing /slash
character, mv
shall treat this as an error and no source_file operands will be processed.
In the second synopsis form, mv
shall move each file named by a source_file operand to a destination file in the existing directory named by the target_dir operand, or referenced if target_dir is a symbolic link referring to an existing directory. The destination path for each source_file shall be the concatenation of the target directory, a single /slash
character if the target did not end in a /slash
, and the last pathname component of the source_file. This second form is assumed when the final operand names an existing directory.
Então, se *
for expandido para: