Bash: Bagaimana cara membaca satu baris sekaligus dari output suatu perintah?

49

Saya mencoba membaca output dari sebuah perintah di bash menggunakan a while loop.

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

Output yang saya dapatkan

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

Setelah ini saya mencoba

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

tapi itu menghasilkan kesalahan test.sh: line 5: ./test.py: Permission denied.

Jadi, bagaimana saya membacanya baris demi baris karena saya pikir saat ini sedang menghirup seluruh baris sekaligus.

Output yang dibutuhkan:

./test.py
./test1.py
./out1
./test.sh
./out
./out2
./hello
RanRag
sumber
3
Saya sarankan membaca Bash FAQ 01 - banyak info berguna dan saran tentang perangkap yang harus dihindari.
jw013
Untuk while readbagian ini, lihat Memahami IFS dan pertanyaan yang ditautkan di sana.
Gilles 'SANGAT berhenti menjadi jahat'
Untuk menggunakan find, lihat Bagaimana saya bisa menggunakan dua perintah bash di -exec dari perintah find? atau Menjalankan fungsi yang ditentukan pengguna dalam panggilan -exec find (yang sebagian besar merupakan duplikat dari pertanyaan ini).
Gilles 'SANGAT berhenti menjadi jahat'

Jawaban:

54

Ada kesalahan, kamu < <(command)tidak perlu<<<$(command)

< <( )adalah Substitusi Proses , $()adalah substitusi perintah dan <<<merupakan string di sini .

Gilles Quenot
sumber
2
@RanRag Berhenti berusaha mengatur $( )segalanya! Itu sintaks untuk substitusi perintah , yang hanya satu cara untuk menggunakan output perintah. Pipa dan proses substitusi dan string di sini adalah yang lain, dan mereka semua memiliki sintaks yang berbeda, secara alami. Anda tidak boleh mem-parsing nama file kecuali Anda benar-benar tahu apa yang Anda lakukan.
jw013
Terima kasih itu bekerja akan membaca lebih lanjut tentang Process Substitution.
RanRag
@ jw013: Saya pemula bash. Di masa depan akan menyimpan saran Anda dalam pikiran saya.
RanRag
13

Perhatikan bahwa tidak ada yang menghentikan nama file dari berisi karakter baris baru. Cara kanonik untuk menjalankan perintah untuk setiap file yang ditemukan oleh find adalah.

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

Dan jika Anda ingin semuanya dilakukan dalam bash:

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

Juga, cara kanonik untuk memanggil perintah "baca" dalam skrip (jika Anda tidak ingin melakukan pemrosesan ekstra pada input) adalah:

IFS= read -r var

-radalah untuk berhenti readmemperlakukan karakter backslash khusus (sebagai karakter pelarian untuk pemisah dan baris baru), Dan IFS = untuk mengatur daftar pemisah ke string kosong untuk read(jika tidak jika ada karakter spasi putih dalam daftar itu, mereka akan dilucuti dari awal dan akhir input).

Menggunakan loop di shell biasanya merupakan ide yang buruk (bukan bagaimana hal-hal dilakukan dalam shell di mana Anda membuat beberapa alat bekerja secara kolektif dan bersamaan untuk suatu tugas daripada menjalankan satu atau lebih alat ratusan kali secara berurutan).

Stéphane Chazelas
sumber