Eu quero fazer algo repetidamente em uma lista de arquivos. Os arquivos nas perguntas têm espaços em seus nomes:
david@david: ls -l
total 32
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha
-rw-rw-r-- 1 david david 0 Mai 8 11:55 haha~
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (3rd copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (4th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (5th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (6th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (7th copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (another copy)
-rw-rw-r-- 1 david david 13 Mai 8 11:55 haha (copy)
Agora eu quero declarar cada um desses arquivos:
david@david: echo '
for file in $(ls)
do
stat $file
done' | bash
(Eu uso echo e um pipe para escrever comandos de várias linhas.)
Quando faço isso, ele funciona corretamente nos arquivos que não possuem espaços em seus nomes. Mas os outros ...
stat: cannot stat ‘(another’: No such file or directory
stat: cannot stat ‘copy)’: No such file or directory
Alterar $(ls)
para "$(ls)"
ou $file
para "$file"
não funciona. O que eu posso fazer?
Editar:
echo '
for files in *
do
stat "$files"
done' | bash
faz o truque! Como sou iniciante no bash, quero manter as coisas o mais simples possível - nada tentando escapar dos espaços, usando xargs
ou a solução read -r
, embora eles resolvam o problema.
Como alguns perguntaram: Sim, usar isso em vez de stat *
é estranho. Mas eu só queria encontrar uma maneira geral de aplicar o mesmo comando em vários nomes de arquivos no bash, usando um loop for. Então, stat
poderia significar gzip
, gpg
ou rm
.
fonte
stat *
? (;-)Respostas:
A citação múltipla do
echo '
está complicando a coisa.Você pode apenas usar:
Mas também
... e se você deseja coletar os arquivos e aplicar o comando (por quê?), você pode prosseguir (mas tenha cuidado com o arquivo que contém novas linhas ... (1))
... e se você quiser arquivos ocultos também, use-os
* .*
como padrão, mas lembre-se disso.
e..
estará no conjunto .Como um aparte, você não deve analisar a
ls
saída .(1) mas se você tiver nomes de arquivos com novas linhas, você merece um pouco ... ;-)
fonte
for f in *; do command "$f"; done
. Nunca analisels
, certamente nunca faça isso emfor
loop e por que usarecho
?do
,|
,&&
etc, você pode continuar na nova linha. Ou isso ou use heredocs. Não há razão para usarecho
e também pode causar problemas.Em uma observação lateral: você pode dividir comandos longos / complicados em várias linhas adicionando um espaço seguido por uma barra invertida e pressionando Entertoda vez que quiser começar a escrever em uma nova linha, em vez de bifurcar vários processos usando
echo [...] | bash
; Além disso, você deve colocar$file
aspas duplas, para impedir astat
quebra no caso de nomes de arquivos que contenham espaços:O problema é que se
$(ls)
expande para uma lista de nomes de arquivos contendo espaços, e o mesmo acontecerá também com"$(ls)"
.Mesmo resolvendo esse problema, esse método ainda será interrompido nos nomes de arquivos que contêm barras invertidas e nos nomes de arquivos que contêm novas linhas (conforme indicado por terdon).
Uma solução para os dois problemas seria canalizar a saída de
find
umwhile
loopread -r
para que, a cada iteração,read -r
armazene uma linha defind
saída em$file
:fonte
ls
. Sempre.ls
. Existem maneiras melhores e mais robustas. Você também pode ler o seguinte: Por que * não * analisar `ls`? para mais detalhes.ls
, éfor
-for
itera sobre (IFS separados) palavras dado depois que oin
keyword`for
els
.for f in *
ficaria bem, por exemplo.Use o bom e velho
find
trabalho com arquivos ocultos, novas linhas e espaços.ou qualquer outro em vez de
stat
fonte
Como R, eu já encontrei uma solução alternativa em R:
Eu sei, é loucura. Eu gostaria que a saída de
ls
fosse mais fácil de analisar ... R pode lidar com espaços, porque dir () retorna um valor de caractere citado. Qualquer coisa entre as aspas é um nome de arquivo válido com espaços.fonte
for f in *
, pressionar enter e continuar em uma nova linhado
:, pressione enter novamentestat "$f"
, digite novamente,done
digite. É um comando dividido bem em 4 linhas e não quebra em nenhum tipo de nome de arquivo.Eu já encontrei outras instâncias de problemas de espaço em branco em loops, então o comando (imo mais robusto) a seguir é o que geralmente uso. Também se encaixa perfeitamente em tubos.
Você pode combinar isso
grep
ou usarfind
:fonte
-E
opçãogrep
, sem a qual ela não será reconhecida+
em uma expressão regular. Mesmo assim, é um fracasso e uma falha nesta questão, pois vocêgrep
remove nomes de arquivos que contêm espaços . Também remove nomes de arquivos contendo dígitos (numerais) e pontuação (por exemplo, parênteses), como fazem os exemplos da pergunta. E isso nem menciona nomes de arquivos que contêm nova linha ou começam com-
(traço).Esta resposta resolverá o problema de análise
ls
e cuidará dos backspaces e das novas linhasTente isso, ele resolverá o seu problema usando o Internal Field Separator IFS.
Mas você também pode resolvê-lo rapidamente sem precisar analisar a saída usando
fonte
IFS
aqui: citar a variável deve ser suficiente para impedir a divisão de palavras, com certeza?ls
.Em vez disso, você pode renomear seus arquivos substituindo o espaço por algum outro caractere, como sublinhado, para se livrar desse problema:
Para fazer isso, execute o comando facilmente:
fonte