O que faz 'mv ./*' sem especificar o destino?

26

Esqueci acidentalmente de especificar o destino antes de apertar a tecla Return. Onde mv ./* sem especificar o destino move os arquivos e diretórios no diretório atual para?

    
por Tim 23.07.2013 / 23:49

5 respostas

38

Se o último argumento era um diretório, você acabou de mover todos os arquivos e diretórios em seu diretório de trabalho atual (exceto aqueles cujos nomes começam com pontos) para esse diretório. Se houver dois arquivos, o primeiro arquivo pode ter sobrescrito o segundo arquivo.

Aqui estão algumas demonstrações:

Mais de dois arquivos e o último argumento é um arquivo

$ mkdir d1 d2 d3
$ touch a b c e
$ mv *
mv: target 'e' is not a directory

Mais de dois arquivos e o último argumento é um diretório

$ mkdir d1 d2 d3
$ touch a b c
$ mv -v *
'a' -> 'd3/a'
'b' -> 'd3/b'
'c' -> 'd3/c'
'd1' -> 'd3/d1'
'd2' -> 'd3/d2'

Dois arquivos

$ touch a b
$ mv -v *
'a' -> 'b'

Mais explicações

O shell expande o glob ( * ) em argumentos para mv . O glob é geralmente expandido em ordem alfabética. mv sempre vê uma lista de arquivos e diretórios. Nunca vê o próprio globo.

O comando mv suporta dois tipos de movimentação. Um é mv file ... directory . O outro é mv old-file-name new-file-name (ou mv old-file-name directory/new-file-name ).

    
por 23.07.2013 / 23:55
30

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
0mv
mv *
+ mv file1 file2 file3 file4 file5 folder
folder/
0file1
sh -cxv 'cd *; mv *'; ls . *
0file2
cd *; mv *
+ cd folder
+ mv file1 file2 file3 file4 file5
mv: target ‘file5’ is not a directory

.:
folder/

folder:
file1  file2  file3  file4  file5
0file3
mv [-if] source_file target_file

mv [-if] source_file... target_dir
0file4
touch file1 file2 file3 file4 file5
mkdir folder
0file5
sh -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
0mv
mv *
+ mv file1 file2 file3 file4 file5 folder
folder/
0file1
sh -cxv 'cd *; mv *'; ls . *
0file2
cd *; mv *
+ cd folder
+ mv file1 file2 file3 file4 file5
mv: target ‘file5’ is not a directory

.:
folder/

folder:
file1  file2  file3  file4  file5
0file3
mv [-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:

  • dois arquivos

    • Você deve ter apenas um arquivo, que é o primeiro renamed para o segundo após o segundo ser unlinked .
  • um ou mais arquivos seguidos por um diretório ou um link para um

    • Você deve ter apenas um diretório ou um link para um, que é onde todos os conteúdos anteriores de seus pais acabaram de ser movidos.
  • qualquer outra coisa

    • Você deve receber uma mensagem de erro e um suspiro de alívio satisfatório.
por 22.08.2014 / 05:35
19

Primeiro, o shell expande ./* para todos os arquivos no diretório atual (exceto os arquivos que começam com um ponto).

  • se não houver ou apenas um arquivo: mv falha
  • se houver dois arquivos: o primeiro é movido para o segundo (que, portanto, se perde)
  • se houver mais de dois arquivos:
    • se o último for um diretório: todos os arquivos são movidos para este diretório
    • caso contrário, mv falhará.
por 23.07.2013 / 23:57
4

Quando você digitar mv ./* , seu shell expandirá ./* antes de executar mv .

Algumas coisas a serem observadas:

  • Se ./* for expandido em menos de dois argumentos, mv produzirá, logicamente, um erro.
  • ./* normalmente se expande em cada arquivo (incluindo o diretório) presente no diretório atual e não iniciando com um ponto.
  • Você pode controlar o que ./* expande lendo a documentação do seu shell ( man 7 glob é um ponto de entrada para o tópico). Conchas diferentes terão diferentes opções.
por 24.07.2013 / 00:16
1

What does mv * do?

Aqui está uma resposta mais curta:

O shell expande o curinga * para uma lista de conteúdo do diretório. Então o shell passa essa lista completa para o comando. O comando nunca vê * .

O comando mv file1 file2 ... filen directory moverá o arquivo1 ... filen para o diretório.

Exemplo

Aqui eu faço um diretório de teste contendo três arquivos

$ mkdir t
$ cd t
$ echo a>a; echo b>b; echo c>c
$ ls
a  b  c

Você não pode mover vários arquivos em um único arquivo

$ mv *
mv: target 'c' is not a directory

Vamos adicionar um subdiretório

$ mkdir d

Você pode mover vários arquivos para um subdiretório

$ mv *
$ ls
d
$ ls d
a  b  c
    
por 22.08.2014 / 13:01