First I'll make a test base - 5 files and one folder:
touch file1 file2 file3 file4 file5
mkdir folder
Em seguida, executarei um comando de teste. A -v
opção especifica que eu quero que todos os comandos que o shell execute sejam impressos stderr
. A -x
opção especifica que eu quero o mesmo impresso stderr
- mas quero que seja feito depois que o comando for avaliado, mas antes que o shell o execute.
sh -cxv 'echo mv *'
SAÍDA
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 após a *
expansão é echo mv
seguido por todos esses arquivos e a pasta.
Por padrão, o shell expandirá globs como:
sh -cxv 'echo file[1-5]'
SAÍDA
echo file[1-5]
+ echo file1 file2 file3 file4 file5
file1 file2 file3 file4 file5
Este é um resultado da set [+-]f
função glob:
sh -cxvf 'echo file[1-5]'
SAÍDA
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 *
palavra uma lista de argumentos de todos os arquivos no diretório atual, classificados de acordo com a localidade. Ele faz o syscall exec(ve)
for mv
(essencialmente) com esta lista de argumentos anexada. Então, mv
obtém todos os argumentos conforme o shell os observa e os classifica. Além strace
de ver esses efeitos, você pode usar a depuração novamente como:
sh -s -- mv * <<\SCRIPT
sed -n l /proc/$$/cmdline
echo "$@"
SCRIPT
SAÍDA
sh\000-s\000--\000mv\000file1\000file2\000file3\000file4\000file5\000folder\
\000$
mv file1 file2 file3 file4 file5 folder
E portably:
( PS4= IFS=/; set -x mv *; : "/$*/" ) 2>&1
SAÍDA
: /mv/file1/file2/file3/file4/file5/folder/
Basicamente, o shell é executado 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 for invocado com mais de dois argumentos - da mesma maneira que ln
é (porque, de fato, são ferramentas incrivelmente semelhantes na função subjacente) .
Já chega echo
:
sh -cxv 'mv *' ; ls
SAÍDA
mv *
+ mv file1 file2 file3 file4 file5 folder
folder/
Todos os arquivos foram movidos para o argumento final - porque é uma pasta. Agora, e se não for uma pasta?
sh -cxv 'cd *; mv *'; ls . *
SAÍDA
cd *; mv *
+ cd folder
+ mv file1 file2 file3 file4 file5
mv: target ‘file5’ is not a directory
.:
folder/
folder:
file1 file2 file3 file4 file5
É assim que o POSIX especifica mv
deve se comportar nesse caso:
mv [-if] source_file target_file
mv [-if] source_file... target_dir
No primeiro formulário de sinopse, o mv
utilitário deve mover o arquivo nomeado pelo operando source_file para o destino especificado pelo target_file . Esse primeiro formulário de sinopse é assumido quando o operando final não nomeia um diretório existente e não é um link simbólico referente a um diretório existente. Nesse caso, se o source_file nomear um arquivo não-diretório e o target_file terminar com um /slash
caractere à direita , mv
tratará isso como um erro e nenhum operando do source_file será processado.
No segundo formulário de sinopse, mv
mova cada arquivo nomeado por um operando source_file para um arquivo de destino no diretório existente nomeado pelo operando target_dir ou referenciado se target_dir for um link simbólico referente a um diretório existente. O caminho de destino para cada arquivo de origem deve ser a concatenação do diretório de destino, um único /slash
caractere se o destino não terminar em ae/slash
o último componente do nome do caminho do arquivo de origem . Este segundo formulário é assumido quando o operando final nomeia um diretório existente.
Portanto, se se *
expandir para: