Estou tentando fazer eco de todos os arquivos /foo
, preservando os espaços nos diretórios listados. Atualmente, um diretório intitulado "bar1 bar2"
ecoará como /path/to/foo/bar1/
então /bar2
em file.txt
todas as novas linhas. Eu preciso ecoar /path/to/foo/bar1 bar2/file.txt
ou /path/to/foo/bar1\ bar2/file.txt
.
for file in $(find /path/to/foo/ -type f); do
if [[ ... ]]; then
echo "${file}"
fi
done
Também tentei:
for file in $(find /path/to/foo/ -type f | sed 's/ /\ /g'); do
if [[ ... ]]; then
echo "${file}"
echo "$file" # variations i've tried
echo $file # variations i've tried
echo ${file}
otherCommand "${file}"
fi
done
Respostas:
O problema não é com
$file
, é com o seu uso de$(find…)
.As variáveis regulares e as
$(…)
substituições de processo estão sujeitas exatamente ao mesmo tipo de divisão de palavras. Se você colocar$(find…)
aspas duplas, obterá uma única palavra com toda a saída. Caso contrário, ele será dividido em qualquer espaço em branco - não apenas em novas linhas ou em algum outro limite mágico que você provavelmente esperava.Uma parte importante do exposto acima é que as escapes de barra invertida não são processadas ao expandir a variável. É apenas dividido em espaços e é isso.
(Em outras palavras, citar
$file
não ajudou porque nunca teve o valor correto em primeiro lugar!)Se você deseja processar todos os arquivos recursivamente, suas opções são:
Leia a saída de
find
outra maneira:(Observação: os nomes dos arquivos também podem incluir novas linhas tecnicamente , portanto, você pode se proteger contra isso, por mais raro que seja:
Para pequenas quantidades de arquivos, use os curingas estendidos do bash:
Com diretórios grandes, a vantagem
find | while read
é que ele é transmitido - seu script processará os resultados à medida que forem chegando. Enquanto isso, os$(find)
curingas e os curingas precisam coletar tudo na memória primeiro e só então retornar os resultados (possivelmente maciços).Por outro lado, o uso de um pipe afeta todo o
while
loop (não apenas oread
comando); portanto, se você deseja executar algo que deseje ler a entrada do teclado, é necessário fornecer manualmente o stdin original, por exemplovim "$file" < /dev/tty
.fonte
... | IFS= read ...
; sem isso,read
removerá os espaços em branco iniciais e finais (o que é raro, mas perfeitamente legal em nomes de arquivos). Além disso, observe que o pipe faz com que o loop seja executado no subshell s, portanto, quaisquer variáveis definidas nele não estarão disponíveis fora do loop. Consulte BashFAQ # 20: "Como posso encontrar e lidar com segurança com nomes de arquivos contendo novas linhas, espaços ou ambos?" para mais dicas e truques.ls
oufind -print
.