Bash: Como ler uma linha de cada vez a partir da saída de um comando?


49

Eu estou tentando ler a saída de um comando no bash usando a while loop.

while read -r line
do
    echo "$line"
done <<< $(find . -type f)

A saída que eu recebi

ranveer@ranveer:~/tmp$ bash test.sh
./test.py ./test1.py ./out1 ./test.sh ./out ./out2 ./hello
ranveer@ranveer:~/tmp$ 

Depois disso eu tentei

$(find . -type f) | 
while read -r line
do
    echo "$line"
done 

mas gerou um erro test.sh: line 5: ./test.py: Permission denied.

Então, como eu leio linha por linha, porque acho que atualmente ela está consumindo toda a linha de uma só vez.

Saída necessária:

./test.py
./test1.py
./out1
./test.sh
./out
./out2
./hello

3
Sugiro ler o Bash FAQ 01 - muitas informações e conselhos úteis sobre armadilhas a serem evitadas.
— jw013

Para a while readparte, consulte Noções básicas sobre o IFS e as perguntas vinculadas lá.
— Gilles 'SO- stop be evil'

Respostas:


54

Há um erro, você < <(command)não precisa<<<$(command)

< <( )é uma substituição de processo , $()é uma substituição de comando e <<<é uma string aqui .


2
@RanRag Pare de tentar $( )contornar tudo! Essa é a sintaxe da substituição de comandos , que é apenas uma maneira de usar a saída de comandos. Tubos, substituição de processo e strings aqui são outros, e todos eles têm sintaxe diferente, naturalmente. Você não deve analisar os nomes dos arquivos, a menos que saiba realmente o que está fazendo.
— jw013

Obrigado, funcionou vai ler mais sobre Process Substitution.
— 21812 RanRag

@ jw013: Sou iniciante no bash. No futuro, manterá sua sugestão em minha mente.
— RanRag

13

Observe que não há nada que impeça que os nomes de arquivos contenham caracteres de nova linha. A maneira canônica de executar um comando para cada arquivo encontrado por find é.

find . -type f -exec cmd {} \;

E se você quiser que as coisas sejam feitas no bash:

find . -type f -exec bash -c '
  for file do
    something with "$file"
  done' bash {} +

Além disso, a maneira canônica de chamar o comando "read" nos scripts (se você não deseja que ele faça um processamento extra na entrada) é:

IFS= read -r var

-ré parar readde tratar caracteres de barra invertida especialmente (como um caractere de escape para separadores e nova linha), E IFS = para definir a lista de separadores para a cadeia vazia para read(caso contrário, se houver espaço em branco estava nessa lista, eles seriam retirados do início e fim da entrada).

Usar loops em shells geralmente é uma má ideia (não como as coisas são feitas nos shells, nas quais você faz várias ferramentas trabalharem coletivamente e simultaneamente a uma tarefa, em vez de executar uma ou mais ferramentas centenas de vezes em sequência).

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.