( jw013 telah memberikan jawaban yang benar , tetapi saya ingin menunjukkan beberapa masalah lagi.)
Sisi kanan pipa berjalan di subkulitnya sendiri di bash (dan juga sebagian besar shell lainnya, pengecualiannya adalah ATT ksh dan zsh). Jadi Anda mengatur variabel lingkungan di subkulit yang mengeksekusi loop, tetapi tidak dalam proses shell utama.
Di sini ada perbaikan yang mudah yaitu mengganti penggunaan yang tidak berguna cat
dengan pengalihan:
while read …; do …; done <"$1"
Secara umum, jika Anda perlu memproses input, letakkan loop dan sisa skrip (setidaknya bagian yang membutuhkan variabel yang diubah) di dalam blok.
grep '^foo_' "$1" | {
while read …; do …; done
do_something
}
Perhatikan bahwa secara umum, while read line; do …
tidak cukup iterate pada jalur input. Lihat Mengapa while IFS= read
sering digunakan, bukan IFS=; while read..
? untuk penjelasan. Ungkapan yang benar untuk beralih pada jalur input adalah
while IFS= read -r line; do …
Di sini, penghapusan spasi putih depan dan akhir tidak menjadi masalah karena seharusnya tidak ada input sampel yang diberikan, tetapi Anda mungkin perlu -r
menghindari ekspansi backslash. Mungkin saja while read line
sebenarnya cara yang benar untuk membaca input Anda (tapi saya curiga hanya jika nilai variabel tidak mengandung baris baru). Mungkin juga format input Anda ambigu atau lebih rumit untuk diuraikan. Periksa apa yang terjadi jika salah satu nilai variabel berisi karakter baris baru, kutipan ganda atau garis miring terbalik.
Satu titik di mana Anda benar-benar mengacaukan input adalah ketika Anda mengekstrak nilainya:
val=`echo ${kv#*=} | sed 's/^"\|"$//g'`
Pertama, Anda perlu menggunakan tanda kutip ganda sekitar substitusi variabel: echo "${kv#*=}"
; jika tidak, shell melakukan pemisahan kata dan nama file generasi (yaitu globbing) di atasnya. Kedua, echo
bukan cara yang dapat diandalkan untuk mencetak string, karena beberapa string yang dimulai dengan a -
diperlakukan sebagai opsi, dan beberapa shell melakukan ekspansi backslash pada argumen. Cara yang andal dan portabel untuk mencetak string adalah printf %s "${kv#*=}"
. Juga, jika nilainya mencakup beberapa baris, program sed akan bertindak pada masing-masing secara terpisah, yang mana salah. Ini diperbaiki, tapi ada metode yang lebih mudah, yaitu dengan menggunakan fasilitas manipulasi string shell: val=${kv#*=}; val=${val#\"}; val=${val%\"}
.
Dengan asumsi bahwa input Anda diurai dengan benar dengan sebuah dataran read
, berikut ini adalah cara yang lebih sederhana untuk menguraikannya, mengambil keuntungan dari IFS
pengaturan untuk membagi setiap baris:
while read name value; do
value=${value#\"}; value=${value%\"}
export name="$value"
done <"$1"
key=${kv%%=*}
lebih baik daripadakey=${kv%=*}
karena %% dan ## berbeda dari% dan # untuk apakah bagian yang dihapus adalah bagian yang dapat dilepas terpendek atau terpanjang