ใบสั่งเอาท์พุทที่มีการทดแทนกระบวนการ


11

นี่คือสิ่งที่ฉันมักจะทำเพื่อเรียกใช้grepและwcบนไฟล์โดยไม่ต้องสแกนสองครั้ง

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

อย่างไรก็ตามอัตราผลตอบแทนนี้

EXEC LITERAL
32

บางครั้งและ

32
EXEC LITERAL

ในเวลาอื่น ๆ (เอาต์พุตจากgrepนำหน้าเอาต์พุตจากwcในอินสแตนซ์แรกและกลับกันในวินาที)

ในขณะที่มีการเปลี่ยนเส้นทางและอธิบายไฟล์

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

ฉันมักจะได้รับเสมอ

EXEC LITERAL
32

ฉันชอบที่คำสั่งเอาท์พุทสามารถคาดเดาได้ แต่รับประกันด้วยแนวทางที่สองหรือไม่?

คำตอบ:


4

ทั้งใน

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

และ:

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

ของทั้งหมดtee, grepและwcจะเริ่มต้นพร้อมกัน สิ่งที่สำคัญคือสิ่งที่เกิดขึ้นในตอนท้าย

wcจะพิมพ์ผลลัพธ์เฉพาะเมื่อเห็นจุดสิ้นสุดไฟล์ในอินพุตมาตรฐาน ในกรณีแรกนั่นคือเมื่อจบการทำงานteeเนื่องจากteeจะปิดfdลงที่ปลายอีกด้านของไพพ์ที่wcอ่านจาก (เริ่มต้นโดยการทดแทนกระบวนการ) ไม่มีการรับประกันว่าgrepจะอ่านอินพุตทั้งหมดในเวลานั้นให้เขียนเอาต์พุตของมันเพียงอย่างเดียว (เนื่องจากท่อสามารถเก็บข้อมูลได้ค่อนข้างมากและwcน่าจะเร็วกว่าgrep)

ในกรณีที่สองwcจะเห็นจุดสิ้นสุดไฟล์เมื่อผู้เขียนทั้งหมดไปยังไปป์ที่กำลังอ่านจากปิดส่วนท้ายของไพพ์ ในกรณีนั้นมีนักเขียนหลายคน tee(ผ่าน fd เปิด/dev/fd/3และผ่าน fd 3) และgrepซึ่งfdเปิด 3 ไปยังท่อไปที่wc(แม้ว่าจะไม่ได้ใช้ประโยชน์จากมันให้เขียนเพียงอย่างเดียว) ภายใน{มีแนวโน้มที่จะก่อให้เกิดกระบวนการ subshell พิเศษที่จะมีfd3 เปิดและจะรอให้ทั้งสองและteegrep

นั่นหมายความว่าwcจะเขียนหมายเลขบรรทัดหลังจากที่grepออกแล้วเท่านั้น

หากคุณเขียนวิธีที่ถูกต้องนั่นคือปิด fds ที่ไม่ต้องการเปิด:

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

จากนั้นคำสั่งจะไม่ได้รับการรับประกันในเชลล์ที่ปรับกระบวนการย่อยให้เหมาะสม อย่างไรก็ตามเชลล์เพียงตัวเดียวที่ฉันรู้ว่ามันใช้ksh93แต่ksh93ใช้ซ็อกเก็ตคู่สำหรับไพพ์ดังนั้นมัน/dev/fd/3จะไม่ทำงานบน Linux อย่างน้อย

หากต้องการดูว่ากระบวนการใดที่กำลังทำงานอยู่คุณสามารถแทนที่grepด้วย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

ด้วยbash, คุณสามารถเห็นกระบวนการเชลล์พิเศษ, และคุณสามารถดูได้ว่ามันมีไปป์ที่เปิดใน fd 3 ด้วย:

$ (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

ขอบคุณ ใน "ตัวอย่างที่เหมาะสม" ของคุณสิ่งที่grep LITERAL >&4 3>&- 4>&-หมายความว่า fd 4 ดูเหมือนจะใช้งานและปิดแล้ว
iruvar

@ 1_CR หลังจาก>&4นั้นย่อมาจาก1>&4คือgrepfd 1 และ 4 ชี้ไปที่ทรัพยากรเดียวกัน (stdout เริ่มต้นของเชลล์) grepไม่จำเป็นต้องเปิด fd 4 เพื่ออะไร มันไม่ได้ทำอะไรกับมันดังนั้นเราจึงปิดด้วย4>&-
Stéphane Chazelas

บรรทัดคำสั่งสุดท้ายนั่นคือเวทมนตร์ที่คลุมเครือ

-1

เพื่อรับการใช้คำสั่งที่คาดการณ์ได้

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

บางทีฉันไม่ชัดเจนพอ ฉันหมายถึงลำดับที่คาดเดาได้ในแง่ของลำดับของเอาต์พุตคำสั่ง (เช่นเอาต์พุตจาก grep ก่อนที่จะส่งออกจาก wc) ฉันไม่ต้องการเอาต์พุตเรียงลำดับที่รวมกัน
iruvar

เพิ่งพบgnu.org/software/bash/manual/bashref.html#Command-Groupingจะบอกฉันว่าด้วยตัวดำเนินการ {} ที่คุณแน่ใจ (ในกรณีนี้) ที่คุณทำ <file.txt tee / dev / fd เป็นครั้งแรก / 3 | grep LITERAL> & 4; และเมื่อสิ่งนี้เสร็จสิ้นคุณเรียกว่า wc ดังนั้นเพื่อตอบคำถามเดิมของคุณใช่มันรับประกันความเข้าใจของฉันแล้ว
Thorsten Staerk

1
@ThorstenStaerk คุณสามารถเพิ่มข้อมูลพิเศษที่คุณพบในคำตอบของคุณได้หรือไม่?
terdon

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.