Cara menyalurkan input ke Bash sementara loop dan mempertahankan variabel setelah loop berakhir

88

Bash memungkinkan untuk menggunakan: cat <(echo "$FILECONTENT")

Bash juga memungkinkan untuk menggunakan: while read i; do echo $i; done </etc/passwd

untuk menggabungkan dua sebelumnya, ini dapat digunakan: echo $FILECONTENT | while read i; do echo $i; done

Masalah dengan yang terakhir adalah bahwa ia membuat sub-shell dan setelah variabel while loop berakhir itidak dapat diakses lagi.

Pertanyaanku adalah:

Bagaimana mencapai sesuatu seperti ini: while read i; do echo $i; done <(echo "$FILECONTENT")atau dengan kata lain: Bagaimana saya bisa yakin itu ibertahan while loop?

Harap perhatikan bahwa saya mengetahui menyertakan pernyataan while ke dalam {}tetapi ini tidak menyelesaikan masalah (bayangkan Anda ingin menggunakan loop sementara dalam fungsi dan mengembalikan ivariabel)

Wakan Tanka
sumber
Terkait: stackoverflow.com/questions/37229058/… . Menjelaskan semua opsi termasuk proses substitusi yang disebutkan di bawah dan lastpipedan pro dan kontra mereka.
ivan_pozdeev

Jawaban:

117

Notasi yang benar untuk Proses Substitusi adalah:

while read i; do echo $i; done < <(echo "$FILECONTENT")

Nilai terakhir yang iditetapkan dalam loop kemudian tersedia saat loop berakhir. Alternatifnya adalah:

echo $FILECONTENT | 
{
while read i; do echo $i; done
...do other things using $i here...
}

Kawat gigi adalah operasi pengelompokan I / O dan tidak membuat subkulit sendiri. Dalam konteks ini, mereka adalah bagian dari pipeline dan oleh karena itu dijalankan sebagai subkulit, tetapi ini karena |, bukan { ... }. Anda menyebutkan ini dalam pertanyaan. AFAIK, Anda dapat melakukan pengembalian dari dalam ini ke dalam fungsi.


Bash juga menyediakan shoptbawaan dan salah satu dari banyak opsinya adalah:

lastpipe

Jika disetel, dan kontrol tugas tidak aktif, shell akan menjalankan perintah terakhir dari pipeline yang tidak dijalankan di latar belakang dalam lingkungan shell saat ini.

Jadi, menggunakan sesuatu seperti ini dalam skrip membuat modfied sumtersedia setelah loop:

FILECONTENT="12 Name
13 Number
14 Information"
shopt -s lastpipe   # Comment this out to see the alternative behaviour
sum=0
echo "$FILECONTENT" |
while read number name; do ((sum+=$number)); done
echo $sum

Melakukan ini pada baris perintah biasanya menjalankan kesalahan 'kontrol pekerjaan tidak aktif' (yaitu, pada baris perintah, kontrol pekerjaan aktif). Pengujian ini tanpa menggunakan skrip gagal.

Juga, seperti dicatat oleh Gareth Rees dalam jawabannya , terkadang Anda dapat menggunakan string di sini :

while read i; do echo $i; done <<< "$FILECONTENT"

Ini tidak membutuhkan shopt; Anda mungkin dapat menyimpan proses dengan menggunakannya.

Jonathan Leffler
sumber
Maafkan ketidaktahuan saya. Saya tahu ini adalah solusi yang tepat dan saya telah menandainya sebagai jawaban sehingga berhasil untuk saya. Tapi sekarang ketika saya menjalankan while read i; do echo $i; done < <(cat /etc/passwd); echo $iItu tidak kembali baris terakhir dua kali. Apa yang saya lakukan salah?
Wakan Tanka
@ WakanTanka: Saya harus bereksperimen sedikit ... Saya yakin jawabannya adalah pembacaan yang gagal disetel ulang imenjadi kosong, jadi gema setelah pengulangan menggemakan baris kosong.
Jonathan Leffler
1
Kudos untuk referensi Proses Substitusi. Saya tidak menyadarinya.
Atcold
@ Wakan Tanka: mendapatkan hasil yang sama seperti milik Anda, saya while read i; do x=$i; done < <(cat /etc/passwd); echo i=$i; echo x=$xpernah bekerja, // mungkinkah perilaku bash hari-hari itu berubah?
yurenchen
Anda seorang penyelamat hidup! Sudah berjam-jam mencari solusi serupa. Milik Anda adalah satu-satunya yang berhasil.
Tepuk
0

Fungsi ini membuat duplikat file jpg sebanyak $ NUM (bash)

function makeDups() {
NUM=$1
echo "Making $1 duplicates for $(ls -1 *.jpg|wc -l) files"
ls -1 *.jpg|sort|while read f
do
  COUNT=0
  while [ "$COUNT" -le "$NUM" ]
  do
    cp $f ${f//sm/${COUNT}sm}
    ((COUNT++))
  done
done
}
Steve
sumber