Bash: Jak czytać jedną linię na raz z wyjścia polecenia?

49

Próbuję odczytać dane wyjściowe polecenia w bash za pomocą while loop.

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

Mam wynik

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

Po tym próbowałem

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

ale wygenerował błąd test.sh: line 5: ./test.py: Permission denied.

Jak mam to odczytać wiersz po wierszu, ponieważ myślę, że obecnie rozmazuje całą linię naraz.

Wymagana moc wyjściowa:

./test.py
./test1.py
./out1
./test.sh
./out
./out2
./hello
RanRag
źródło
3
Sugeruję przeczytanie Bash FAQ 01 - wiele przydatnych informacji i porad na temat pułapek, których należy unikać.
jw013,
W tej while readczęści zobacz Zrozumienie IFS i powiązane z nim pytania.
Gilles „SO- przestań być zły”
Aby finddowiedzieć się więcej na temat używania , zobacz Jak mogę użyć dwóch poleceń bash w -exec polecenia find? lub Wykonywanie funkcji zdefiniowanej przez użytkownika w wywołaniu find -exec (którego to pytanie jest w większości duplikatem).
Gilles „SO- przestań być zły”

Odpowiedzi:

54

Jest to błąd, trzeba < <(command)nie<<<$(command)

< <( )jest podstawieniem procesu , $()jest podstawieniem polecenia i <<<jest ciągiem tutaj .

Gilles Quenot
źródło
2
@RanRag Przestań próbować $( )ominąć wszystko! To jest składnia podstawiania poleceń , która jest tylko jednym sposobem na użycie danych wyjściowych polecenia. Rurki i podstawianie procesów oraz ciągi tutaj są inne i wszystkie mają naturalnie inną składnię. I tak nie powinieneś analizować nazw plików, chyba że naprawdę wiesz, co robisz.
jw013,
Dzięki, że zadziałało, przeczytaj więcej o Process Substitution.
RanRag
@ jw013: Jestem początkującym bash. W przyszłości będę pamiętać o twoich sugestiach.
RanRag
13

Zauważ, że nic nie stoi na przeszkodzie, aby nazwy plików zawierały znaki nowej linii. Kanonicznym sposobem uruchomienia polecenia dla każdego pliku znalezionego przez find jest.

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

A jeśli chcesz robić rzeczy w bash:

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

Kanoniczny sposób wywoływania w skryptach polecenia „czytaj” (jeśli nie chcesz, aby przetwarzał dane wejściowe):

IFS= read -r var

-rma przestać readtraktować znaki odwrotnego ukośnika specjalnie (jako znak zmiany znaczenia dla separatorów i nowej linii), a IFS = ustawić listę separatorów na pusty ciąg znaków read(w przeciwnym razie, jeśli jakikolwiek znak spacji byłby na liście, byłyby usuwane z początek i koniec wprowadzania).

Używanie pętli w powłokach jest zwykle złym pomysłem (nie chodzi o to, jak się to dzieje w powłokach, w których kilka narzędzi działa wspólnie i jednocześnie do zadania, zamiast uruchamiać jedno lub więcej narzędzi setki razy po kolei).

Stéphane Chazelas
źródło