กระบวนการทดแทนและท่อ


86

ฉันสงสัยว่าจะเข้าใจสิ่งต่อไปนี้อย่างไร:

การวาง stdout ของคำสั่งลงใน stdin ของอีกอันเป็นเทคนิคที่ทรงพลัง แต่ถ้าคุณต้องการไพพ์ stdout ของหลายคำสั่งล่ะ? นี่คือที่มาทดแทนกระบวนการ

กล่าวอีกนัยหนึ่งกระบวนการทดแทนสามารถทำสิ่งที่ท่อสามารถทำได้หรือไม่

กระบวนการทดแทนสามารถทำอะไรได้บ้าง แต่ไปป์ไม่ได้

คำตอบ:


134

วิธีที่ดีในการหาความแตกต่างระหว่างพวกเขาคือทำการทดลองเล็กน้อยบนบรรทัดคำสั่ง แม้จะมีความคล้ายคลึงกันทางสายตาในการใช้<ตัวละคร แต่ก็ทำสิ่งที่แตกต่างจากการเปลี่ยนเส้นทางหรือไพพ์

ลองใช้dateคำสั่งสำหรับการทดสอบ

$ date | cat
Thu Jul 21 12:39:18 EEST 2011

นี่เป็นตัวอย่างที่ไม่มีจุดหมาย แต่ก็แสดงให้เห็นว่าcatยอมรับเอาท์พุทของdateบน STDIN และคายมันออกมา ผลลัพธ์เดียวกันสามารถทำได้โดยการทดแทนกระบวนการ:

$ cat <(date)
Thu Jul 21 12:40:53 EEST 2011

อย่างไรก็ตามสิ่งที่เพิ่งเกิดขึ้นหลังฉากแตกต่างกัน แทนที่จะได้รับกระแส STDIN catจริง ๆ แล้วผ่านชื่อของไฟล์ที่จำเป็นต้องเปิดและอ่าน คุณสามารถดูขั้นตอนนี้โดยใช้แทนechocat

$ echo <(date)
/proc/self/fd/11

เมื่อ cat ได้รับชื่อไฟล์มันจะอ่านเนื้อหาของไฟล์ให้เรา ในทางกลับกันเสียงก้องเพิ่งแสดงชื่อไฟล์ที่ส่งผ่านให้เรา ความแตกต่างนี้ชัดเจนมากขึ้นถ้าคุณเพิ่มการทดแทนเพิ่มเติม:

$ cat <(date) <(date) <(date)
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011
Thu Jul 21 12:44:45 EEST 2011

$ echo <(date) <(date) <(date)
/proc/self/fd/11 /proc/self/fd/12 /proc/self/fd/13

เป็นไปได้ที่จะรวมการทดแทนกระบวนการ (ซึ่งสร้างไฟล์) และการเปลี่ยนเส้นทางอินพุต (ซึ่งเชื่อมต่อไฟล์กับ STDIN):

$ cat < <(date)
Thu Jul 21 12:46:22 EEST 2011

มันดูค่อนข้างเหมือนเดิม แต่คราวนี้ cat ผ่าน STDIN stream แทนชื่อไฟล์ คุณสามารถเห็นสิ่งนี้ได้โดยลองกับ echo:

$ echo < <(date)
<blank>

เนื่องจากเสียงก้องไม่ได้อ่าน STDIN และไม่ผ่านการโต้แย้งเราจึงไม่ได้อะไรเลย

ไพพ์และอินพุตเปลี่ยนทิศทางการเลื่อนเนื้อหาไปยังสตรีม STDIN การทดแทนโปรเซสรันคำสั่งบันทึกเอาต์พุตไปยังไฟล์ชั่วคราวพิเศษจากนั้นส่งชื่อไฟล์นั้นแทนคำสั่ง คำสั่งใดก็ตามที่คุณใช้จะถือว่าเป็นชื่อไฟล์ โปรดทราบว่าไฟล์ที่สร้างไม่ได้เป็นไฟล์ปกติ แต่ไปป์ที่มีชื่อที่จะถูกลบโดยอัตโนมัติเมื่อมันไม่จำเป็นอีกต่อไป


ถ้าฉันเข้าใจ corretly, tldp.org/LDP/abs/html/process-sub.html#FTN.AEN18244กล่าวว่าการทดแทนกระบวนการสร้างไฟล์ชั่วคราวไม่ใช่ชื่อไพพ์ เท่าที่ฉันรู้ชื่อไม่ได้สร้างไฟล์ชั่วคราว การเขียนไปที่ไพพ์ไม่เกี่ยวข้องกับการเขียนลงดิสก์: stackoverflow.com/a/6977599/788700
Adobe

ฉันรู้ว่าคำตอบนี้ถูกต้องเพราะมันใช้คำว่าgrok : D
aqn

2
@Adobe [[ -p <(date) ]] && echo trueคุณสามารถยืนยันว่าการเปลี่ยนตัวผู้เล่นกระบวนการแฟ้มชั่วคราวผลิตเป็นท่อชื่อด้วย: สิ่งนี้สร้างขึ้นtrueเมื่อฉันรันด้วย bash 4.4 หรือ 3.2
De Novo

24

ฉันควรจะคิดว่าคุณกำลังพูดถึงbashหรือเปลือกขั้นสูงอื่น ๆ เพราะเปลือก POSIX ไม่ได้มีการเปลี่ยนตัวกระบวนการ

bash รายงานหน้าคู่มือ:

การทดแทนกระบวนการการทดแทน
กระบวนการได้รับการสนับสนุนบนระบบที่รองรับชื่อ pipes (FIFO) หรือวิธี / dev / fd ของการตั้งชื่อไฟล์ที่เปิด มันใช้รูปแบบของ <(รายการ) หรือ> (รายการ) รายการกระบวนการจะถูกรันด้วยอินพุตหรือเอาต์พุตที่เชื่อมต่อกับ FIFO หรือไฟล์บางไฟล์ใน / dev / fd ชื่อของไฟล์นี้ถูกส่งผ่านเป็นอาร์กิวเมนต์ไปยังคำสั่งปัจจุบันเป็นผลมาจากการขยายตัว หากใช้แบบฟอร์ม> (รายการ) การเขียนไปยังไฟล์จะเป็นการป้อนข้อมูลให้กับรายการ หากใช้รูปแบบ <(รายการ) ไฟล์ที่ส่งผ่านเป็นอาร์กิวเมนต์ควรอ่านเพื่อรับเอาต์พุตของรายการ

เมื่อพร้อมใช้งานจะทำการทดแทนกระบวนการพร้อมกันพร้อมพารามิเตอร์และการขยายตัวแปรการทดแทนคำสั่งและการขยายเลขคณิต

กล่าวอีกนัยหนึ่งและจากมุมมองเชิงปฏิบัติคุณสามารถใช้นิพจน์ดังต่อไปนี้

<(commands)

เป็นชื่อไฟล์สำหรับคำสั่งอื่น ๆ ที่ต้องการไฟล์เป็นพารามิเตอร์ หรือคุณสามารถใช้การเปลี่ยนเส้นทางสำหรับไฟล์ดังกล่าว:

while read line; do something; done < <(commands)

กลับไปที่คำถามของคุณดูเหมือนว่าสำหรับฉันแล้วการทดแทนกระบวนการและท่อมีไม่มากนัก

หากคุณต้องการไพพ์ตามลำดับเอาต์พุตของหลายคำสั่งคุณสามารถใช้หนึ่งในรูปแบบต่อไปนี้:

(command1; command2) | command3
{ command1; command2; } | command3

แต่คุณยังสามารถใช้การเปลี่ยนเส้นทางในการทดแทนกระบวนการ

command3 < <(command1; command2)

ท้ายที่สุดถ้าcommand3ยอมรับพารามิเตอร์ไฟล์ (ในการทดแทน stdin)

command3 <(command1; command2)

ดังนั้น <() และ <<() ให้ผลเหมือนกันใช่ไหม
solfish

@solfish: ไม่ใช่ exacllty: firse สามารถใช้ได้ทุกที่ที่คาดว่าชื่อไฟล์ที่สองคือการเปลี่ยนเส้นทางอินพุตสำหรับชื่อไฟล์นั้น
enzotib

23

นี่คือสามสิ่งที่คุณสามารถทำได้ด้วยการทดแทนกระบวนการที่เป็นไปไม่ได้

กระบวนการหลายอินพุต

diff <(cd /foo/bar/; ls) <(cd /foo/baz; ls)

มีเพียงวิธีการทำเช่นนี้กับท่อไม่เป็น

รักษา STDIN

สมมติว่าคุณมีดังต่อไปนี้:

curl -o - http://example.com/script.sh
   #/bin/bash
   read LINE
   echo "You said ${LINE}!"

และคุณต้องการเรียกใช้โดยตรง ต่อไปนี้ล้มเหลวอย่างน่าสังเวช Bash ใช้ STDIN เพื่ออ่านสคริปต์แล้วดังนั้นอินพุตอื่นจึงเป็นไปไม่ได้

curl -o - http://example.com/script.sh | bash 

แต่วิธีนี้ทำงานได้อย่างสมบูรณ์

bash <(curl -o - http://example.com/script.sh)

การทดแทนกระบวนการขาออก

โปรดทราบว่าการทดแทนกระบวนการทำงานด้วยวิธีอื่นด้วย ดังนั้นคุณสามารถทำสิ่งนี้:

(ls /proc/*/exe >/dev/null) 2> >(sed -n \
  '/Permission denied/ s/.*\(\/proc.*\):.*/\1/p' > denied.txt )

นั่นเป็นตัวอย่างที่ซับซ้อน แต่มันส่งstdoutไป/dev/nullในขณะที่pip stderrไปยัง sed script เพื่อแยกชื่อของไฟล์ที่มีข้อผิดพลาด "Permission ปฏิเสธ" ปรากฏขึ้นจากนั้นส่งผลลัพธ์ THOSE ไปยังไฟล์

โปรดทราบว่าคำสั่งแรกและการเปลี่ยนเส้นทางstdoutอยู่ในวงเล็บ ( subshell ) เพื่อให้เฉพาะผลลัพธ์ของคำสั่งนั้นถูกส่งไป/dev/nullและไม่ยุ่งกับส่วนที่เหลือของบรรทัด


มันเป็นที่น่าสังเกตว่าในdiffตัวอย่างเช่นคุณอาจต้องการที่จะดูแลเกี่ยวกับกรณีที่อาจล้มเหลว:cd diff <(cd /foo/bar/ && ls) <(cd /foo/baz && ls)
phk

"ในขณะที่ piping stderr": ไม่ใช่จุดที่นี่ไม่ได้เป็นpiping แต่จะผ่านไฟล์ Fifo หรือไม่?
Gauthier

@Gauthier ไม่; คำสั่งที่ได้รับการทดแทนไม่ได้อยู่กับ fifo แต่มีการอ้างอิงไปยังไฟล์อธิบาย ดังนั้น "echo <(echo)" ควรให้ผลเช่น "/ dev / fd / 63" ซึ่งเป็นอุปกรณ์ตัวอักษรพิเศษที่อ่านหรือเขียนจาก FD หมายเลข 63
tylerl

10

หากคำสั่งใช้รายการไฟล์เป็นอาร์กิวเมนต์และประมวลผลไฟล์เหล่านั้นเป็นอินพุต (หรือเอาต์พุต แต่ไม่ปกติ) ไฟล์เหล่านั้นแต่ละไฟล์สามารถเป็นไพพ์ที่มีชื่อหรือ / dev / fd pseudo-file ที่จัดเตรียมไว้อย่างโปร่งใสโดยกระบวนการย่อย:

$ sort -m <(command1) <(command2) <(command3)

สิ่งนี้จะ "ไพพ์" เอาต์พุตของสามคำสั่งเพื่อเรียงลำดับเนื่องจากการเรียงลำดับสามารถใช้รายการของไฟล์อินพุตบนบรรทัดรับคำสั่ง


1
IIRC ไวยากรณ์ <(คำสั่ง) เป็นคุณลักษณะเฉพาะของ bash
Philomath

@Philomath: มันอยู่ใน ZSH ด้วย
คาเลบ

ZSH มีทุกอย่าง ... (หรืออย่างน้อยก็พยายาม)
Philomath

@Philomath: การทดแทนกระบวนการดำเนินการอย่างไรในเชลล์อื่น
camh

4
@Phomomath <()เช่นเดียวกับคุณสมบัติเชลล์ขั้นสูงหลายอย่างเดิมเป็นคุณสมบัติ ksh และได้รับการรับรองโดย bash และ zsh psubเป็นฟีเจอร์ของปลาโดยเฉพาะไม่มีอะไรเกี่ยวข้องกับ POSIX
Gilles

3

ควรสังเกตว่าการทดแทนกระบวนการไม่ได้ จำกัด อยู่ที่แบบฟอร์ม<(command)ซึ่งใช้เอาต์พุตcommandเป็นไฟล์ มันสามารถอยู่ในรูปแบบ>(command)ที่ฟีดไฟล์เป็นอินพุตcommandเช่นกัน นี่ยังกล่าวถึงในใบเสนอราคาของคู่มือทุบตีในคำตอบของ @ enzotib

สำหรับdate | catตัวอย่างข้างต้นคำสั่งที่ใช้การทดแทนกระบวนการของแบบฟอร์ม>(command)เพื่อให้ได้ผลเช่นเดียวกันคือ

date > >(cat)

โปรดทราบว่า>ก่อนหน้า>(cat)นี้มีความจำเป็น นี่สามารถแสดงให้เห็นได้อย่างชัดเจนอีกครั้งechoเหมือนในคำตอบของ @ Caleb

$ echo >(cat)
/dev/fd/63

ดังนั้นไม่ต้องเสริม>, date >(cat)จะเป็นเช่นเดียวกับdate /dev/fd/63ที่จะพิมพ์ข้อความไปยัง stderr

สมมติว่าคุณมีโปรแกรมที่ใช้เวลาเพียงชื่อไฟล์เป็นพารามิเตอร์และไม่ได้ดำเนินการหรือstdin stdoutฉันจะใช้สคริปต์ oversimplified psub.shเพื่อแสดงสิ่งนี้ เนื้อหาของpsub.shคือ

#!/bin/bash
[ -e "$1" -a -e "$2" ] && awk '{print $1}' "$1" > "$2"

โดยพื้นฐานแล้วจะทดสอบว่าอาร์กิวเมนต์ทั้งคู่เป็นไฟล์ (ไม่ใช่ไฟล์ปกติ) และถ้าเป็นกรณีนี้ให้เขียนฟิลด์แรกของแต่ละบรรทัดของ"$1"การ"$2"ใช้ awk จากนั้นคำสั่งที่รวมทุกอย่างที่กล่าวถึงคือ

./psub.sh <(printf "a a\nc c\nb b") >(sort)

สิ่งนี้จะพิมพ์

a
b
c

และเทียบเท่า

printf "a a\nc c\nb b" | awk '{print $1}' | sort

แต่สิ่งต่อไปนี้จะไม่ทำงานและเราต้องใช้การทดแทนกระบวนการที่นี่

printf "a a\nc c\nb b" | ./psub.sh | sort

หรือรูปแบบที่เทียบเท่า

printf "a a\nc c\nb b" | ./psub.sh /dev/stdin /dev/stdout | sort

หาก./psub.shยังอ่านstdinนอกเหนือจากที่กล่าวถึงข้างต้นแล้วรูปแบบที่เทียบเท่านั้นไม่มีอยู่และในกรณีนั้นไม่มีอะไรที่เราสามารถใช้แทนการทดแทนกระบวนการได้ (แน่นอนว่าคุณสามารถใช้ไพพ์ที่มีชื่อหรือไฟล์ชั่วคราวได้ เรื่อง)

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