Mengapa saya harus meletakkan perintah baca ke dalam subkulit saat menggunakan pipeline [duplikat]
Perintah tersebut df .
dapat menunjukkan kepada kita perangkat mana yang kita gunakan. Sebagai contoh,
me@ubuntu1804:~$ df .
Filesystem 1K-blocks Used Available Use% Mounted on
/dev/sdb1 61664044 8510340 49991644 15% /home
Sekarang saya ingin mendapatkan tali itu /dev/sdb1
.
Saya mencoba seperti ini tetapi tidak berhasil df . | read a; read a b; echo "$a"
:, perintah ini memberi saya keluaran kosong. Tapi df . | (read a; read a b; echo "$a")
akan bekerja seperti yang diharapkan.
Saya agak bingung sekarang.
Saya tahu itu (read a; read a b; echo "$a")
subkulit, tapi saya tidak tahu mengapa saya harus membuat subkulit di sini. Menurut pemahaman saya, x|y
akan mengarahkan output x
ke input dari y
. Mengapa read a; read a b; echo $a
tidak bisa mendapatkan input tetapi subkulit bisa?
Jawaban
Masalah utama di sini adalah mengelompokkan perintah dengan benar. Subkulit adalah masalah sekunder.
x|y
akan mengarahkan keluaranx
ke masukan dariy
Ya, tetapi x | y; z
tidak akan mengalihkan keluaran dari x
ke y
dan z
.
Di df . | read a; read a b; echo "$a"
, pipeline hanya menghubungkan df .
dan read a
, perintah lain tidak memiliki koneksi ke pipeline tersebut. Anda harus mengelompokkan read
s bersama-sama: df . | { read a; read a b; }
atau df . | (read a; read a b)
agar pipa dapat dihubungkan ke keduanya.
Namun, sekarang muncul masalah subkulit: perintah dalam pipeline dijalankan di subkulit, jadi menyetel variabel di dalamnya tidak memengaruhi shell induk. Jadi echo
perintahnya harus berada di subkulit yang sama dengan read
s. Jadi: df . | { read a; read a b; echo "$a"; }
.
Sekarang apakah Anda menggunakan ( ... )
atau { ...; }
tidak membuat perbedaan khusus di sini karena perintah dalam pipeline tetap dijalankan di subkulit.
Alternatifnya adalah dengan menggunakan substisi proses :
{ read header; read filesystem rest; } < <(df .)
echo "$filesystem"
The <(...)
proses substitusi mengeksekusi script yang terkandung (dalam subkulit), tapi itu bertindak seperti nama file, sehingga Anda perlu pertama <
untuk mengarahkan isi (yang merupakan output dari script) ke dalam script bersiap. Perintah yang dikelompokkan dieksekusi di rak saat ini ;.
Ini bisa sulit untuk membuat ini mudah dibaca, tetapi Anda dapat meletakkan sembarang spasi kosong ke dalam kawat gigi dan proses penggantian.
{
read header
read filesystem rest
} < <(
df .
)
echo "$filesystem"
Dan mungkin lebih mudah menggunakan alat eksternal untuk mengekstrak sistem file:
filesystem=$( df . | awk 'NR == 2 {print $1}' )
Perintah pertama Anda
df . | read a; read a b; echo "$a"
efektif ditafsirkan sebagai
( df . | read a ) ; read a b; echo "$a"
Jadi pipeline hanya dimasukkan ke dalam read a
perintah.
Karena Anda ingin banyak pembacaan dari pipeline, maka Anda perlu mengelompokkan perintah bersama.
Sekarang tidak harus menjadi subkulit; itu bisa menjadi pengelompokan ..
bash-4.2$ df | { read a ; read a b ; echo $a ; }
devtmpfs
Lebih umum Anda mungkin menginginkan loop
bash-4.2$ df | while read a > do > read a b > echo $a
> done
devtmpfs
tmpfs
/dev/vda3
/dev/vdb
Ada masalah sekunder dengan bash
dan sisi kanan pipeline menjalankan subkulit, jadi $a
$b
nilainya tidak dapat diakses di luar while
loop, tetapi itu masalah yang berbeda!