Mengapa saya harus meletakkan perintah baca ke dalam subkulit saat menggunakan pipeline [duplikat]

Nov 30 2020

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|yakan mengarahkan output xke input dari y. Mengapa read a; read a b; echo $atidak bisa mendapatkan input tetapi subkulit bisa?

Jawaban

8 muru Dec 01 2020 at 03:10

Masalah utama di sini adalah mengelompokkan perintah dengan benar. Subkulit adalah masalah sekunder.

x|yakan mengarahkan keluaran xke masukan dariy

Ya, tetapi x | y; ztidak akan mengalihkan keluaran dari xke ydan 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 reads 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 echoperintahnya harus berada di subkulit yang sama dengan reads. 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.

3 glennjackman Dec 01 2020 at 03:10

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}' )
2 StephenHarris Dec 01 2020 at 03:11

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 aperintah.

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 bashdan sisi kanan pipeline menjalankan subkulit, jadi $a $bnilainya tidak dapat diakses di luar whileloop, tetapi itu masalah yang berbeda!