di bash, baca setelah pipa tidak menetapkan nilai

22

Sunting: judul asli "dibaca gagal di bash"

Dengan ksh saya menggunakan baca sebagai cara mudah untuk memisahkan nilai:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 
2 1
$

Tapi gagal di bash:

$ echo 1 2 3 4 5 | read a b dump
$ echo $b $a 

$

Saya tidak menemukan alasan di halaman manual mengapa gagal, tahu?

Emmanuel
sumber
2
Ini dibahas (agak tidak jelas) di halaman 024 dari FAQ Greg's Bash .
Scott

Jawaban:

27

bash menjalankan sisi kanan pipeline dalam konteks subshell , jadi perubahan variabel (yang memang readdilakukan) tidak dipertahankan - mereka mati ketika subshell melakukannya, di akhir perintah.

Sebagai gantinya, Anda dapat menggunakan substitusi proses :

$ read a b dump < <(echo 1 2 3 4 5)
$ echo $b $a
2 1

Dalam hal ini, readberjalan di dalam shell utama kami, dan perintah penghasil output kami berjalan di subkulit. The <(...)sintaks menciptakan subkulit dan menghubungkan output ke pipa, yang kita mengarahkan ke input dari readdengan biasa <operasi . Karena readdijalankan di shell utama kita, variabel diatur dengan benar.

Seperti yang ditunjukkan dalam komentar, jika tujuan Anda adalah untuk membagi string menjadi variabel, Anda dapat menggunakan string di sini :

read a b dump <<<"1 2 3 4 5"

Saya berasumsi ada lebih dari itu, tetapi ini adalah pilihan yang lebih baik jika tidak ada.

Michael Homer
sumber
3
Atau bahkan read a b dump <<< '1 2 3 4 5'.
choroba
Terima kasih untuk semua, tapi saya perhatikan bahwa mksh (di cygwin) melakukan hal yang sama dengan bash.
Emmanuel
@Michael Homer Penjelasan yang bagus! Saya menemukan satu contoh lain yang bisa menjelaskan bahwa setiap perintah dalam pipa berjalan di subkulit sendiri: cat /etc/passwd | (read -r line ; echo $line). Tapi berikutnya echodari $lineyang tidak dalam pipa put apa-apa di layar, karena nilai itu ada hanya antara tanda kurung (subkulit). Semoga, ini membantu seseorang.
Yurij Goncharuk
17

Ini bukan bashbug yang POSIXmemungkinkan keduanya bashdan kshperilaku, yang mengarah ke ketidaksesuaian malang yang Anda amati.

http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_12

Selain itu, setiap perintah pipa multi-perintah berada dalam lingkungan subkulit; sebagai ekstensi, bagaimanapun, setiap atau semua perintah dalam pipa dapat dieksekusi di lingkungan saat ini. Semua perintah lain harus dijalankan di lingkungan shell saat ini.

Namun, dengan bash 4.2dan yang lebih baru, Anda dapat mengatur lastpipeopsi dalam skrip non interaktif untuk mendapatkan hasil yang diharapkan, misalnya:

#!/bin/bash

echo 1 2 3 4 5 | read a b dump
echo before: $b $a 
shopt -s lastpipe
echo 1 2 3 4 5 | read a b dump
echo after: $b $a 

Keluaran:

before:
after: 2 1
Jlliagre
sumber
1
+1 terima kasih atas info "lastpipe". maaf atas keterlambatannya
Emmanuel
1
masalahnya lastpipeadalah tidak bekerja di shell lain (mis. dash). pada dasarnya tidak ada cara untuk melakukan portable ini dari menjalankan segala sesuatu dalam subkulit itu, lihat stackoverflow.com/questions/36268479/…
anarcat
1
@ anarcat Ini benar tetapi pertanyaan yang diajukan di sini adalah tentang bash.
jlliagre
@anarcat: Ini mungkin berubah di masa depan karena ada keinginan untuk mengubah POSIX karena alasan lain (Status PIPE) lihat di sini: unix.stackexchange.com/questions/476834/... Mengganti shell lain bukanlah hal sepele, butuh beberapa bulan bagi saya untuk menulis ulang parser dan interpeter pada Bourne Shell (bosh) untuk mengimplementasikan perilaku ksh modern yang lebih cepat.
schily