Cara membaca baris lengkap dalam loop 'untuk' dengan spasi

130

Saya mencoba menjalankan forloop untuk file dan saya ingin menampilkan seluruh baris. Melainkan hanya menampilkan kata terakhir saja. Saya ingin garis lengkap.

for j in `cat ./file_wget_med`

do
echo $j

done

hasil setelah dijalankan:

Found.

Ini data saya:

$ cat file_wget_med
2013-09-11 14:27:03 ERROR 404: Not Found.
pengguna192118
sumber
terkait: stackoverflow.com/questions/9084257/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Jawaban:

179

forloop terpecah ketika melihat spasi putih seperti spasi, tab, atau baris baru. Jadi, Anda harus menggunakan IFS (Pemisah Bidang Internal) :

IFS=$'\n'       # make newlines the only separator
for j in $(cat ./file_wget_med)    
do
    echo "$j"
done
# Note: IFS needs to be reset to default!
Radu Rădeanu
sumber
5
Dan berikan perhatian pada tanda kutip tunggal pada IFS karena IFS = $ "\ n" akan membagi juga string "nn". Saya menemukan set IFS lebih dapat diandalkan daripada menggunakan sintaks atau fungsi yang lebih rumit.
erm3nda
23
rupanya disarankan untuk unset IFSsesudahnya, sehingga perintah lain tidak terpengaruh dalam shell yang sama ini.
Boris Däppen
2
Apa $artinya ini IFS=$'\n'?
Ren
2
$membuat jeda baris berfungsi di dalam string. Ini juga harus dilakukan dengan local IFS=$'\n'di dalam suatu fungsi.
Andy Ray
1
@Renn yang $'...'dikenal " ANSI-C Quoting "
αғsнιη
82

forloop terpecah pada spasi putih apa pun (spasi, tab, baris baru) secara default; cara termudah untuk bekerja pada satu baris pada satu waktu adalah menggunakan while readloop sebagai gantinya, yang terbagi pada baris baru:

while read i; do echo "$i"; done < ./file_wget_med

Saya akan mengharapkan perintah Anda untuk mengeluarkan satu kata per baris (itulah yang terjadi ketika saya mengujinya dengan file saya sendiri). Jika sesuatu yang lain terjadi, saya tidak yakin apa yang menyebabkannya.

Evilsoup
sumber
8
Ini juga merupakan alternatif yang baik untuk for i in `ls`; do echo $1; done, dengan pipa output ke perintah sementara: ls|while read i; do echo $i; done. Ini berfungsi pada nama file / folder dengan spasi, tanpa perlu mengubah variabel lingkungan. Jawaban yang sangat bagus
jishi
sementara itu tidak menangani baris pertama dan terakhir dengan sangat, tidak sebaik loop untuk dengan IFS
hibah
@ jishi Karena ini terutama dirancang untuk tampilan, dan responsif terhadap ukuran terminal-window dan semacamnya, lstidak dianggap aman untuk digunakan dengan |(operator pipa). findlebih fleksibel selain lebih aman untuk tujuan ini.
SeldomNeedy
3
Ini adalah jawaban yang HEBAT, saya menggunakannya untuk mengubah daftar yang saya miliki menjadi entri PLIST untuk aplikasi iOS yang saya kembangkan di mac OSX. Saya mengganti input file dengan memipkan clipboad menggunakan pbpaste ->pbpaste | while read t; do echo "<string>$t</string>"; done
Big Rich
3
ls -1dapat digunakan dengan |untuk menjamin output yang belum diformat satu per baris
AndreyS Scherbakov
19
#!/bin/bash
files=`find <subdir> -name '*'`
while read -r fname; do
    echo $fname
done <<< "$files"

Diverifikasi bekerja, bukan satu-satunya liner yang mungkin Anda inginkan tetapi tidak mungkin melakukannya dengan elegan.

Mitchell Currie
sumber
1
di sini-string! paling elegan dari semua solusi IMO lainnya
Eliran Malka
5

Berikut adalah sedikit ekstensi untuk jawaban Mitchell Currie, yang saya suka karena cakupan efek samping yang kecil, yang menghindari keharusan menetapkan variabel:

#!/bin/bash
while read -r fname; do
    echo $fname
done <<< "`find <subdir>`"
Ketombe
sumber
1
Anda bisa menghapus -name '*'dan itu akan sama, bukan?
wjandrea
1

Saya akan menulis seperti ini:

cat ./file_wget_med | while read -r j
do
    echo $j
done

karena membutuhkan paling sedikit perubahan dalam skrip asli (kecuali untuk solusi yang menggunakan IFS, tetapi memodifikasi bashperilaku tidak hanya untuk pernyataan kontrol loop ini).

mik
sumber
1
Pipa dan cattidak perlu di sini. whileloop menerima pengalihan baik-baik saja, itulah sebabnya mengapa ini biasanya ditulis sebagaiwhile IFS= read -r line; do...done < input.txt
Sergiy Kolodyazhnyy
0

Mapfile adalah cara yang mudah untuk membaca baris dari file ke dalam array yang diindeks, tidak portabel seperti membaca tetapi sedikit lebih cepat. Dengan menggunakan untuk loop Anda menghindari membuat subkulit.

#!/bin/bash

mapfile -t < file.txt

for line in "${MAPFILE[@]}"; do
    echo $line
done

Perlu diingat saat menggunakan pipa, itu akan menempatkan loop sementara dalam subkulit. Perubahan di dalam variabel while loop like tidak akan merambat ke bagian luar skrip.

Contoh:

#!/bin/bash

a=0
printf %s\\n {0..5} | while read; do
  ((a++))
done
echo $a # 'a' will always be 0.

(Solusi yang lebih baik):

#!/bin/bash

b=0
while read; do
  ((b++))
done < <(printf %s\\n {0..5})

echo $b # 'b' equal to 6 (works as expected).
bac0n
sumber
-1

Dandalf mendekati solusi fungsional, tetapi seseorang TIDAK BISA PERNAH mencoba untuk menetapkan hasil dari jumlah input yang tidak diketahui (yaitu find ~/.gdfuse -name '*') ke variabel! Atau, setidaknya mencoba melakukan hal seperti itu melalui variabel array; jika Anda bersikeras menjadi sangat malas! Jadi, inilah solusi Dandalf dilakukan tanpa manuver berbahaya; dan semuanya dalam satu baris

while read -r fname; do
  echo $fname;
done <<< `find ~/.gdfuse -name '*'
odoncaoa
sumber
Hai @odoncaoa, saya mencoba melakukan perintah find tanpa tanda kutip ganda, tetapi itu membuat semua hasil muncul sebagai satu baris. Saya bingung tentang "jumlah input yang tidak diketahui" dengan perintah ini dan bahaya yang terlibat. Bisakah Anda memberikan contoh bahaya dan bagaimana mereka secara teoritis bisa terjadi? Saya benar-benar tidak ingin malas tentang hal itu, saya hanya lebih nyaman dalam bahasa pemrograman lain.
Dandalf