Dalam program berikut, jika saya mengatur variabel $foo
ke nilai 1 di dalam if
pernyataan pertama , itu bekerja dalam arti bahwa nilainya diingat setelah pernyataan if. Namun, ketika saya mengatur variabel yang sama ke nilai 2 di dalam if
yang ada di dalam while
pernyataan, itu dilupakan setelah while
loop. Berperilaku seperti saya menggunakan semacam salinan variabel $foo
di dalam while
loop dan saya hanya memodifikasi salinan tertentu. Inilah program pengujian lengkap:
#!/bin/bash
set -e
set -u
foo=0
bar="hello"
if [[ "$bar" == "hello" ]]
then
foo=1
echo "Setting \$foo to 1: $foo"
fi
echo "Variable \$foo after if statement: $foo"
lines="first line\nsecond line\nthird line"
echo -e $lines | while read line
do
if [[ "$line" == "second line" ]]
then
foo=2
echo "Variable \$foo updated to $foo inside if inside while loop"
fi
echo "Value of \$foo in while loop body: $foo"
done
echo "Variable \$foo after while loop: $foo"
# Output:
# $ ./testbash.sh
# Setting $foo to 1: 1
# Variable $foo after if statement: 1
# Value of $foo in while loop body: 1
# Variable $foo updated to 2 inside if inside while loop
# Value of $foo in while loop body: 2
# Value of $foo in while loop body: 2
# Variable $foo after while loop: 1
# bash --version
# GNU bash, version 4.1.10(4)-release (i686-pc-cygwin)
bash
while-loop
scope
sh
Eric Lilja
sumber
sumber
SC2030: Modification of foo is local (to subshell caused by pipeline).
Jawaban:
The
while
loop dieksekusi dalam subkulit. Jadi setiap perubahan yang Anda lakukan pada variabel tidak akan tersedia setelah subkulit keluar.Sebagai gantinya, Anda dapat menggunakan string di sini untuk menulis ulang loop sementara agar berada dalam proses shell utama; hanya
echo -e $lines
akan berjalan dalam subkulit:Anda dapat menyingkirkan yang agak jelek
echo
di sini-string di atas dengan memperluas urutan backslash segera saat menetapkanlines
. The$'...'
bentuk mengutip dapat digunakan di sana:sumber
<<< "$(echo -e "$lines")"
ke sederhana<<< "$lines"
tail -f
bukan teks tetap?while read -r line; do echo "LINE: $line"; done < <(tail -f file)
(jelas loop tidak akan berakhir karena terus menunggu input daritail
).while
loop ataufor
loop; alih-alih penggunaan subshell yaitu, dicmd1 | cmd2
,cmd2
dalam subshell. Jadi jikafor
loop dieksekusi dalam subkulit, perilaku tak terduga / bermasalah akan ditampilkan.DIPERBARUI # 2
Penjelasan ada dalam jawaban Blue Moons.
Solusi alternatif:
Menghapuskan
echo
Tambahkan gema di dalam dokumen-di sini
Jalankan
echo
di latar:Redirect ke penanganan file secara eksplisit (Pikirkan ruang di
< <
!):Atau cukup arahkan ke
stdin
:Dan satu untuk
chepner
(menghilangkanecho
):Variabel
$lines
dapat dikonversi ke array tanpa memulai sub-shell baru. Karakter\
dann
harus dikonversi ke beberapa karakter (misalnya karakter baris baru nyata) dan menggunakan variabel IFS (Pemisah Bidang Internal) untuk membagi string menjadi elemen array. Ini dapat dilakukan seperti:Hasilnya adalah
sumber
lines
-satunya tujuan variabel tampaknya adalah untuk memberi makanwhile
loop.for line in $(echo -e $lines); do ... done
Anda adalah pengguna ke-742342 untuk menanyakan FAQ bash ini . Jawabannya juga menjelaskan kasus umum variabel yang diatur dalam subkulit yang dibuat oleh pipa:
sumber
Hmmm ... Saya hampir bersumpah bahwa ini bekerja untuk shell Bourne asli, tetapi tidak memiliki akses ke salinan yang sedang berjalan sekarang untuk memeriksa.
Namun, ada solusi yang sangat sepele untuk masalah ini.
Ubah baris pertama skrip dari:
untuk
Et voila! Pembacaan pada akhir pipa berfungsi dengan baik, dengan asumsi Anda memiliki shell Korn yang diinstal
sumber
Saya menggunakan stderr untuk menyimpan dalam satu lingkaran, dan membaca dari luar. Di sini var i pada awalnya diatur dan dibaca di dalam loop sebagai 1.
Atau gunakan metode heredoc untuk mengurangi jumlah kode dalam subkulit. Perhatikan bahwa nilai i berulang dapat dibaca di luar loop sementara.
sumber
Bagaimana dengan metode yang sangat sederhana
sumber
Ini adalah pertanyaan yang menarik dan menyentuh konsep yang sangat mendasar dalam Bourne shell and subshell. Di sini saya memberikan solusi yang berbeda dari solusi sebelumnya dengan melakukan semacam penyaringan. Saya akan memberikan contoh yang mungkin berguna dalam kehidupan nyata. Ini adalah fragmen untuk memeriksa apakah file yang diunduh sesuai dengan checksum yang dikenal. File checksum terlihat seperti berikut (Menampilkan hanya 3 baris):
Script shell:
Induk dan subkulit berkomunikasi melalui perintah gema. Anda dapat memilih beberapa teks yang mudah diurai untuk shell induk. Metode ini tidak merusak cara berpikir normal Anda, hanya saja Anda harus melakukan beberapa pemrosesan pos. Anda dapat menggunakan grep, sed, awk, dan lainnya untuk melakukannya.
sumber