Nunca, nunca usefor foo in $(cat bar)
. Este é um erro clássico, conhecido como bash pitfall number 1 . Você deve usar:
while IFS= read -r file; do mv -- "$file" "new_place/$file"; done < file_list.txt
Quando você executar o for
loop, o bash irá aplicar wordsplitting ao que se lê, o que significa que a strange blue cloud
vai ser lido como a
, strange
, blue
e cloud
:
$ cat files
a strange blue cloud.txt
$ for file in $(cat files); do echo "$file"; done
a
strange
blue
cloud.txt
Comparado a:
$ while IFS= read -r file; do echo "$file"; done < files
a strange blue cloud.txt
Ou ainda, se você insistir no UUoC :
$ cat files | while IFS= read -r file; do echo "$file"; done
a strange blue cloud.txt
Portanto, o while
loop lerá sua entrada e usará o read
para atribuir cada linha a uma variável. Os IFS=
conjuntos do separador de campos de entrada para NULL * , e a -r
opção read
do impede de interpretar fugas de barra invertida (de modo que \t
é tratado como barra + t
e não como uma guia). O --
depois dos mv
meios "trata tudo depois do - como um argumento e não uma opção", o que permite lidar com nomes de arquivos iniciados -
corretamente.
* Isso não é necessário aqui, estritamente falando, o único benefício nesse cenário é evitar a read
remoção de qualquer espaço em branco à esquerda ou à direita, mas é um bom hábito quando você precisa lidar com nomes de arquivos que contenham caracteres de nova linha ou em geral, quando você precisa lidar com nomes de arquivos arbitrários.
IFS=
não ajudará com novas linhas nos nomes dos arquivos. O formato do arquivo aqui simplesmente não permite nomes de arquivos com novas linhas.IFS=
é necessário para nomes de arquivos que começam com espaço ou tabulação (ou qualquer outro caractere que não seja a nova linha $ IFS contida anteriormente)ls
oufind
com substituições de comando quando houver maneiras melhores e mais confiáveis de fazer isso.$(cat file)
ficaria bem quando bem feito. É tão difícil "fazer o certo" com um loop while de leitura quanto com um loop for + $ (...). Veja minha resposta.Isso
$(cat file_list.txt)
nos shells POSIX, comobash
no contexto da lista, é o operador split + glob (zsh
apenas faz a parte dividida conforme o esperado).Ele se divide em caracteres de
$IFS
(por padrão, SPC , TAB e NL) e fica glob a menos que você desative as globbing por completo.Aqui, você deseja dividir apenas a nova linha e não a parte glob, portanto deve ser:
Isso também tem a vantagem (sobre um
while read
loop) de descartar linhas vazias, preservar uma linha não terminada à direita e preservarmv
o stdin (necessário em caso de solicitações, por exemplo).Ele tem a desvantagem de que todo o conteúdo do arquivo deve ser armazenado na memória (várias vezes com shells como
bash
ezsh
).Com algumas conchas (
ksh
,zsh
e em menor graubash
), você pode otimizá-lo com em$(<file_list.txt)
vez de$(cat file_list.txt)
.Para fazer o equivalente com um
while read
loop, você precisa:Ou com
bash
:Ou com
zsh
:Ou com GNU
mv
ezsh
:Ou com GNU
mv
e GNUxargs
e ksh / zsh / bash:Mais informações sobre o que significa deixar expansões sem citação em Implicações de segurança de esquecer de citar uma variável em shells bash / POSIX
fonte
$(cat file_list.txt)
lê o arquivo inteiro na memória antes de iterar, enquanto quewhile read ...
apenas lê uma linha por vez. Isso pode ser um problema para arquivos grandes.Em vez de escrever um script, você pode usar o find ..
para o caso em que os arquivos estão localizados no arquivo, você pode fazer o seguinte:
o segundo não tenho certeza ..
Boa sorte
fonte
find
, também poderá usarfind
para tudo. Por que trazerxargs
para isso?find -type f -iname '*.txt' -exec mv {} /folder/to/destination/
.-exec
se comporta perfeitamente com nomes de arquivos arbitrários (incluindo aqueles que contêm espaços, novas linhas ou qualquer outra estranheza). Eu sei comoxargs
funciona, obrigado :) Eu simplesmente não vejo o ponto de chamar um comando separado quandofind
já pode fazer isso por você. Nada de errado com isso, apenas ineficiente.xargs
também tem a desvantagem de derrotar stdin (quemv
precisa de seus prompts). Também é um problema com a solução do @ terdon. Com GNUxargs
e shells com substituição de processo, pode ser atenuado comxargs -0IF -a <(find...) mv F /dest
#! / bin / bash
FILE =
zenity --title "Pick a Movie" --file-selection
para FILE em "$ {FILE [0]}"
omxplayer "$ {FILE [0]}" concluído
! / bin / bash
FILE =
zenity --title "Pick a Song" --file-selection
para FILE em "$ {FILE [0]}"
jogar "$ {FILE [0]}" feito
! / bin / bash
DIR =
zenity --title "Pick a Album" --file-selection --directory
para DIR em "$ {DIR [0]}"
faça o cd "$ {DIR [0]}" && find. -type f -name ' .ogg' -o -name ' .mp3' | sort --version-sort | enquanto lê DIR; jogar "$ DIR" feito pronto
! / bin / bash
DIR =
zenity --title "Pick a Album" --file-selection --directory
para DIR em "$ {DIR [0]}"
faça o cd "$ {DIR [0]}" && find. -type f -name ' .ogg' -o -name ' .mp3' | enquanto lê DIR; jogar "$ DIR" feito pronto
fonte