Mengapa saya mendapatkan nilai berbeda $x
dari cuplikan di bawah ini?
#!/bin/bash
x=1
echo fred > junk ; while read var ; do x=55 ; done < junk
echo x=$x
# x=55 .. I'd expect this result
x=1
cat junk | while read var ; do x=55 ; done
echo x=$x
# x=1 .. but why?
x=1
echo fred | while read var ; do x=55 ; done
echo x=$x
# x=1 .. but why?
Jawaban:
Penjelasan yang tepat telah diberikan oleh jsbillings dan geekosaur , tetapi biarkan saya sedikit memperluasnya.
Di sebagian besar shell, termasuk bash, setiap sisi pipa berjalan dalam subkulit, sehingga setiap perubahan dalam kondisi internal shell (seperti variabel pengaturan) tetap terbatas pada segmen pipa tersebut. Satu-satunya informasi yang dapat Anda peroleh dari subkulit adalah apa yang ditampilkannya (ke keluaran standar dan deskriptor file lainnya) dan kode keluarnya (yang merupakan angka antara 0 dan 255). Misalnya, cuplikan berikut mencetak 0:
Dalam ksh (varian yang berasal dari kode AT&T, bukan varian pdksh / mksh) dan zsh, item terakhir dalam sebuah pipa dieksekusi di shell induk. (POSIX memungkinkan kedua perilaku.) Jadi potongan di atas mencetak 2.
Sebuah idiom yang berguna adalah memasukkan kelanjutan loop sementara (atau apa pun yang Anda miliki di sisi kanan pipa, tetapi loop sementara sebenarnya umum di sini) dalam pipa:
sumber
< <(locate -ber ^\.tag$)
, berkat jawaban yang sedikit tidak jelas dan geekosaurus dan comemnts glenn jackman .. Saya awalnya dalam dilema tentang menerima jawaban, tetapi hasil nett cukup jelas, terutama dengan komentar tindak lanjutAnda mengalami masalah lingkup variabel. Variabel yang didefinisikan dalam loop sementara yang ada di sisi kanan pipa memiliki konteks lingkup lokal mereka sendiri, dan perubahan variabel tidak akan terlihat di luar loop. Loop sementara pada dasarnya adalah sebuah subkulit yang mendapatkan COPY dari lingkungan shell, dan setiap perubahan pada lingkungan akan hilang di akhir shell. Lihat pertanyaan StackOverflow ini .
DIPERBARUI : Saya lalai menunjukkan fakta penting bahwa perulangan while dengan subkulitnya sendiri adalah karena itu merupakan titik akhir dari sebuah pipa, saya telah memperbarui itu dalam jawabannya.
sumber
while
loop sebagai ujung ekor pipa yang melemparkannya ke subkulit.blah|blah|while read ...
, Anda dapat memilikiwhile read ...; done < <(blah|blah)
Seperti disebutkan dalam jawaban lain , bagian-bagian dari pipa berjalan dalam subkulit, sehingga modifikasi yang dilakukan tidak terlihat oleh shell utama.
Jika kami menganggap hanya Bash, ada dua solusi lain selain
cmd | { stuff; more stuff; }
struktur:Alihkan input dari substitusi proses :
Output dari perintah di
<(...)
dibuat agar tampak seolah-olah itu adalah pipa bernama.The
lastpipe
pilihan, yang membuat Bash bekerja seperti ksh, dan menjalankan bagian terakhir dari pipa dalam proses shell utama. Meskipun itu hanya berfungsi jika kontrol pekerjaan dinonaktifkan, yaitu tidak dalam shell interaktif:atau
Substitusi proses tentu saja didukung dalam ksh dan zsh juga. Tetapi karena mereka menjalankan bagian terakhir dari pipa di shell utama, menggunakannya sebagai solusi tidak benar-benar diperlukan.
sumber
itu bisa bekerja.
sumber