Esqueci-me acidentalmente de especificar o destino antes de pressionar a tecla Return. Onde, mv ./*sem especificar o destino, move os arquivos e diretórios no diretório atual para?
"Estou surpreso que mvaceite 1 argumento" Não. Ele aceita todos os argumentos que o shell passou para ele depois de expandir o arquivo *.
glglgl
Respostas:
41
Se o último argumento foi um diretório, você acabou de mover todos os arquivos e diretórios do seu diretório de trabalho atual (exceto aqueles cujos nomes começam com pontos) para esse diretório. Se houvesse dois arquivos, o primeiro arquivo pode ter substituído o segundo.
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. A glob é geralmente expandida em ordem alfabética. mvsempre vê uma lista de arquivos e diretórios. Ele nunca vê a própria bola.
O comando mvsuporta dois tipos de movimento. Um é mv file ... directory. O outro é mv old-file-name new-file-name(ou mv old-file-name directory/new-file-name).
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 -vopção especifica que eu quero que todos os comandos que o shell execute sejam impressos stderr. A -xopçã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.
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 mvseguido por todos esses arquivos e a pasta.
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, mvobtém todos os argumentos conforme o shell os observa e os classifica. Além stracede ver esses efeitos, você pode usar a depuração novamente como:
sh -s -- mv * <<\SCRIPT
sed -n l /proc/$$/cmdline
echo "$@"
SCRIPT
Basicamente, o shell é executado mvcom 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) .
No primeiro formulário de sinopse, o mvutilitá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 /slashcaractere à direita , mvtratará isso como um erro e nenhum operando do source_file será processado.
No segundo formulário de sinopse, mvmova 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 /slashcaractere 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:
dois arquivos
Você deve ter apenas um arquivo, que é o primeiro renamedpara o segundo após o segundo unlinked.
um ou mais arquivos seguidos por último 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.
algo mais
Você deve ter uma mensagem de erro e um suspiro de alívio satisfatório.
sim, usando o velho sh, esqueci a depuração com isso, mas com relação à minha pergunta original, isso significa que o problema é que o shell não é mv? Isso é desagradável, é mais simples do que eu pensava inicialmente, mas é uma verdadeira armadilha.
user2485710
5
@ user2485710, o ponto principal é que, no Unix, o shell é responsável por expandir curingas antes de passar os argumentos da linha de comando para o comando. No Windows, cada comando precisa expandir os próprios curingas. Isso permite que os shells do Unix ofereçam recursos avançados de curinga que funcionam com qualquer comando.
CJM
2
@mikeserv, dado que esses arquivos não eram tão importantes, corri para a limpeza e pulei para a próxima etapa, mas posso confirmar as coisas como elas aparecem agora na minha edição da minha primeira postagem / pergunta.
user2485710
6
@mikeserv tl; dr, *não é um argumento único? Direita? :)
Bernhard
11
@ Bernhard - na verdade, esse é o único caso que não cobri - porque pode ser.
mikeserv
18
Primeiro, o shell se expande ./*para todos os arquivos no diretório atual (exceto arquivos começando com um ponto).
se não houver ou houver apenas um arquivo: mvfalhar
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
Obrigado. como saber qual subdiretório é "o último"?
Tim
7
Basta ligar para echo ./*ver qual ordem seu shell usa (geralmente em ordem alfabética).
Jofel
Se falhar com um arquivo, por que não diz isso?
Númeno
@ Noumenon Eu não entendo o seu comentário. mvretorna uma mensagem de erro se for chamada com apenas um argumento.
Jofel
Você está certo. O mesmo comando da minha história agora dá missing destination file operand after *filename*, embora ontem não tenha sido.
Noumenon
4
Quando você digita mv ./*, seu shell se expande ./*antes de executar mv.
Algumas coisas a serem observadas:
Se ./*for expandido para menos de 2 argumentos mv, logicamente produzirá um erro.
./* normalmente será expandido para todos os arquivos (incluindo diretório) presentes no diretório atual e não começando com um ponto.
Você pode controlar o que se ./*expande lendo a documentação do seu shell ( man 7 globé um ponto de entrada para o tópico). Conchas diferentes terão opções diferentes.
mv file1 file2 ... filen directoryÉ muito pouco provável que o comando tenha algo a ver com isso *.
mikeserv
@ Mike: Minha resposta aponta que o último estágio de expansão do shell efetivamente transforma o último no primeiro. Não saber que isso parece ser a raiz da confusão do OP.
RedGrittyBrick
Sim, mas o que quero dizer é que o shell não se expandirá *a file dirmenos que exista algum código de idioma no dseguinte f.
mikeserv
@mikeserv: Existe um código de idioma, meu exemplo é um recortar e colar do Putty se conectando a um sistema CentOS 5.6 GNU / Linux.
RedGrittyBrick
que local é esse? seu exemplo é o a b c dque não é file ... dir. Eu apenas comentei, porque você não menciona o tipo em nenhum lugar e diz apenas o que *se torna o file ... dirque não acontece.
mv
aceite 1 argumento" Não. Ele aceita todos os argumentos que o shell passou para ele depois de expandir o arquivo*
.Respostas:
Se o último argumento foi um diretório, você acabou de mover todos os arquivos e diretórios do seu diretório de trabalho atual (exceto aqueles cujos nomes começam com pontos) para esse diretório. Se houvesse dois arquivos, o primeiro arquivo pode ter substituído o segundo.
Aqui estão algumas demonstrações:
Mais de dois arquivos e o último argumento é um arquivo
Mais de dois arquivos e o último argumento é um diretório
Dois arquivos
Mais explicações
O shell expande o glob (
*
) em argumentos paramv
. A glob é geralmente expandida em ordem alfabética.mv
sempre vê uma lista de arquivos e diretórios. Ele nunca vê a própria bola.O comando
mv
suporta dois tipos de movimento. Um émv file ... directory
. O outro émv old-file-name new-file-name
(oumv old-file-name directory/new-file-name
).fonte
First I'll make a test base - 5 files and one 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 impressosstderr
. A-x
opção especifica que eu quero o mesmo impressostderr
- mas quero que seja feito depois que o comando for avaliado, mas antes que o shell o execute.SAÍDA
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:
SAÍDA
Este é um resultado da
set [+-]f
função glob:SAÍDA
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 syscallexec(ve)
formv
(essencialmente) com esta lista de argumentos anexada. Então,mv
obtém todos os argumentos conforme o shell os observa e os classifica. Alémstrace
de ver esses efeitos, você pode usar a depuração novamente como:SAÍDA
E portably:
SAÍDA
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 queln
é (porque, de fato, são ferramentas incrivelmente semelhantes na função subjacente) .Já chega
echo
:SAÍDA
Todos os arquivos foram movidos para o argumento final - porque é uma pasta. Agora, e se não for uma pasta?
SAÍDA
É assim que o POSIX especifica
mv
deve se comportar nesse caso:Portanto, se se
*
expandir para:dois arquivos
renamed
para o segundo após o segundounlinked
.um ou mais arquivos seguidos por último por um diretório ou um link para um
algo mais
fonte
sh
, esqueci a depuração com isso, mas com relação à minha pergunta original, isso significa que o problema é que o shell não émv
? Isso é desagradável, é mais simples do que eu pensava inicialmente, mas é uma verdadeira armadilha.*
não é um argumento único? Direita? :)Primeiro, o shell se expande
./*
para todos os arquivos no diretório atual (exceto arquivos começando com um ponto).mv
falharmv
falhará.fonte
echo ./*
ver qual ordem seu shell usa (geralmente em ordem alfabética).mv
retorna uma mensagem de erro se for chamada com apenas um argumento.missing destination file operand after *filename*
, embora ontem não tenha sido.Quando você digita
mv ./*
, seu shell se expande./*
antes de executarmv
.Algumas coisas a serem observadas:
./*
for expandido para menos de 2 argumentosmv
, logicamente produzirá um erro../*
normalmente será expandido para todos os arquivos (incluindo diretório) presentes no diretório atual e não começando com um ponto../*
expande lendo a documentação do seu shell (man 7 glob
é um ponto de entrada para o tópico). Conchas diferentes terão opções diferentes.fonte
Aqui está uma resposta mais curta:
O shell expande o curinga
*
para uma lista do conteúdo do diretório. Em seguida, o shell passa a lista completa para o comando. O comando nunca vê*
.O comando
mv file1 file2 ... filen directory
moverá file1 ... filen para o diretórioExemplo
Aqui eu faço um diretório de teste contendo três arquivos
Você não pode mover vários arquivos em um único arquivo
Vamos adicionar um subdiretório
Você pode mover vários arquivos para um subdiretório
fonte
mv file1 file2 ... filen directory
É muito pouco provável que o comando tenha algo a ver com isso*
.*
afile dir
menos que exista algum código de idioma nod
seguintef
.a b c d
que não éfile ... dir
. Eu apenas comentei, porque você não menciona o tipo em nenhum lugar e diz apenas o que*
se torna ofile ... dir
que não acontece.