Output pesanan dengan substitusi proses

11

Inilah yang biasanya saya lakukan untuk menjalankan grepdan wcpada file tanpa harus memindai dua kali

<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null

Namun, ini menghasilkan

EXEC LITERAL
32

terkadang dan

32
EXEC LITERAL

di waktu lain. (Output dari grepmendahului output dari wcpada instance pertama dan sebaliknya pada yang kedua.)

Di sisi lain, dengan pengalihan dan deskriptor file

{ { <file.txt tee /dev/fd/3 | grep LITERAL >&4; } 3>&1 | wc -l ;} 4>&1 

Saya sepertinya selalu mendapatkan

EXEC LITERAL
32

Saya lebih suka bahwa urutan output dapat diprediksi tetapi apakah dijamin dengan pendekatan kedua?

iruvar
sumber

Jawaban:

4

Di keduanya

<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null

Dan:

{ { <file.txt tee /dev/fd/3 | grep LITERAL >&4; } 3>&1 | wc -l ;} 4>&1

Semua tee, grepdan wcdimulai secara bersamaan. Yang penting adalah apa yang terjadi pada akhirnya.

wchanya akan mencetak hasilnya ketika melihat akhir file pada input standarnya. Dalam kasus pertama, saat itulah teekeluar, karena kemudian teeakan menutup fddi ujung pipa yang wcmembaca (dimulai dengan proses substitusi). Tidak ada jaminan bahwa grepakan membaca semua inputnya pada saat itu, apalagi menuliskan hasilnya (mengingat pipa dapat menampung cukup banyak data dan itu wckemungkinan akan lebih cepat daripada grep)

Dalam kasus kedua, wcakan melihat akhir file ketika semua penulis ke pipa yang dibacanya telah menutup ujung pipa mereka. Namun dalam hal itu, ada beberapa penulis. tee(melalui fd terbuka /dev/fd/3dan melalui fd 3) dan grepyang juga memiliki fd3 terbuka untuk pipa wc(meskipun tidak memanfaatkannya, apalagi menulis untuk itu). Bagian dalam {kemungkinan akan menyebabkan proses subkulit tambahan yang juga akan memiliki fd3 terbuka dan akan menunggu keduanya teedan grep.

Itu berarti bahwa wchanya akan menulis nomor baris setelah grepkeluar.

Apakah Anda menulisnya dengan cara yang benar, yaitu dengan menutup fds yang tidak perlu dibuka:

{ { <file.txt tee /dev/fd/3 4>&- | 
   grep LITERAL >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1

Maka pesanan tidak akan dijamin dalam cangkang yang mengoptimalkan proses subkulit. Namun, satu-satunya shell yang saya tahu yang melakukannya adalah ksh93tetapi ksh93menggunakan pasangan soket untuk pipa, jadi /dev/fd/3tidak akan bekerja di Linux setidaknya.

Untuk melihat proses apa yang sedang berjalan, Anda dapat menggantinya grepdengan ps:

$ { { <file.txt tee /dev/fd/3 4>&- | ps -H >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1
  PID TTY          TIME CMD
 8727 pts/5    00:00:00 bash
 8815 pts/5    00:00:00   bash
 8817 pts/5    00:00:00     tee
 8818 pts/5    00:00:00     ps
 8816 pts/5    00:00:00   wc

Dengan bash, Anda dapat melihat proses shell ekstra, dan Anda dapat melihatnya juga memiliki pipa dibuka pada fd 3 dengan:

$ (p=$BASHPID; { { <file.txt tee /dev/fd/3 4>&- | lsof -ag "$p" -d3 >&4 3>&- 4>&-; } 3>&1 | wc -l 4>&-;} 4>&1)
COMMAND  PID PGID     USER   FD   TYPE DEVICE SIZE/OFF   NODE NAME
bash    9843 9842 chazelas    3w  FIFO    0,8      0t0 153304 pipe
tee     9845 9842 chazelas    3w  FIFO    0,8      0t0 153304 pipe
lsof    9846 9842 chazelas    3r   DIR    0,3        0      1 /proc
Stéphane Chazelas
sumber
Terima kasih. Dalam "contoh yang tepat" Anda, apa grep LITERAL >&4 3>&- 4>&-artinya, fd 4 tampaknya digunakan dan ditutup?
iruvar
@ 1_CR, setelah >&4, kependekan dari 1>&4, grepfd 1 dan 4 menunjuk ke sumber yang sama (stdout awal shell). greptidak perlu memiliki fd 4 terbuka untuk apa pun. Itu tidak melakukan apa-apa dengannya, jadi kami menutupnya dengan4>&-
Stéphane Chazelas
Baris perintah terakhir adalah sihir samar.
-1

Untuk mendapatkan pesanan yang dapat diprediksi, gunakan

(<file.txt  tee >(grep LITERAL) >(wc -l) >/dev/null)|sort
Thorsten Staerk
sumber
Mungkin saya tidak cukup jelas. Maksud saya order dapat diprediksi dalam hal urutan output perintah (yaitu output dari grep sebelum output dari wc). Saya tidak perlu output gabungan diurutkan
iruvar
baru saja menemukan gnu.org/software/bash/manual/bashref.html#Command-Grouping , ia memberi tahu saya bahwa dengan {} operator yang Anda pastikan (dalam hal ini) bahwa Anda pertama kali melakukan <file.txt tee / dev / fd / 3 | grep LITERAL> & 4; dan ketika ini selesai, Anda menelepon wc, jadi untuk menjawab pertanyaan awal Anda, ya dijamin dengan pemahaman saya
Thorsten Staerk
1
@ThorstenStaerk dapatkah Anda menambahkan info tambahan yang Anda temukan pada jawaban Anda?
terdon