ฉันจะรวมสองไพพ์ที่มีชื่อเข้ากับสตรีมอินพุตเดี่ยวใน linux ได้อย่างไร


64

การใช้คุณสมบัติ pipes ( |) ใน Linux ฉันสามารถส่งต่ออินพุตมาตรฐานไปยังสตรีมเอาต์พุตหนึ่งหรือหลาย ๆ

ฉันสามารถใช้teeเพื่อแยกเอาต์พุตเพื่อแยกกระบวนการย่อย

มีคำสั่งให้เข้าร่วมสองกระแสอินพุตหรือไม่

ฉันจะไปเกี่ยวกับเรื่องนี้อย่างไร diff ต่างกันอย่างไร

คำตอบ:


105

ส่วนตัวฉันชอบ (ต้องทุบตีและสิ่งอื่น ๆ ที่เป็นมาตรฐานในการกระจาย Linux มากที่สุด)

รายละเอียดสามารถขึ้นอยู่กับสิ่งที่ทั้งสองสิ่งเอาท์พุทและวิธีที่คุณต้องการผสาน ...

เนื้อหาของ command1 และ command2 หลังจากกันและกันในเอาต์พุต:

cat <(command1) <(command2) > outputfile

หรือถ้าทั้งสองคำสั่งให้ส่งข้อมูลสำรองรุ่นเดียวกันกับที่คุณต้องการเห็นแบบเคียงข้างกัน (ฉันเคยใช้มันกับ snmpwalk; ตัวเลขที่อยู่ข้างหนึ่งและชื่อ MIB):

paste <(command1) <(command2) > outputfile

หรือถ้าคุณต้องการเปรียบเทียบผลลัพธ์ของคำสั่งที่คล้ายกันสองคำสั่ง (เช่นค้นหาในสองไดเรกทอรีที่ต่างกัน)

diff <(command1) <(command2) > outputfile

หรือหากพวกเขาสั่งให้เรียงลำดับผลลัพธ์ให้รวม:

sort -m <(command1) <(command2) > outputfile

หรือเรียกใช้คำสั่งทั้งสองพร้อมกัน (อาจแย่งกันซักหน่อย):

cat <(command1 & command2) > outputfile

ตัวดำเนินการ <() ตั้งค่าไพพ์ที่มีชื่อ (หรือ / dev / fd) สำหรับแต่ละคำสั่งไพพ์เอาต์พุตของคำสั่งนั้นไปยังไพพ์ที่มีชื่อ (หรือ / dev / fd filehandle การอ้างอิง) และส่งชื่อบน commandline มีค่าเท่ากับ> () คุณสามารถทำได้: command0 | tee >(command1) >(command2) >(command3) | command4เพื่อส่งเอาต์พุตของหนึ่งคำสั่งไปยัง 4 คำสั่งอื่นพร้อมกัน


น่ากลัว! ฉันได้อ่าน manpage ของ bash มาหลายครั้งแล้ว แต่ไม่ได้เลือกมาก่อน
Javier

2
คุณสามารถค้นหาข้อมูลอ้างอิงได้ใน [คู่มือการเขียนสคริปต์ขั้นสูงทุบตี] ( tldp.org/LDP/abs/html/process-sub.html ) ที่โครงการเอกสารลินุกซ์
ราคา

3
ฉันก็สามารถที่จะป้องกันไม่ให้สายการสลับกันโดยผ่านท่อgrep --line-buffered- มีประโยชน์สำหรับการควบgrep'ไอเอ็นจีtailของแฟ้มบันทึกหลาย ดูstackoverflow.com/questions/10443704/line-buffered-cat
RubyTuesdayDONO

16

คุณสามารถต่อท้ายสองไอน้ำเข้าด้วยcatกันตามที่กอริลลาแสดง

คุณยังสามารถสร้าง FIFO ส่งออกคำสั่งไปยังนั้นแล้วอ่านจาก FIFO ด้วยโปรแกรมอื่น ๆ :

mkfifo ~/my_fifo
command1 > ~/my_fifo &
command2 > ~/my_fifo &
command3 < ~/my_fifo

มีประโยชน์อย่างยิ่งสำหรับโปรแกรมที่จะเขียนหรืออ่านไฟล์หรือรวมโปรแกรมที่เอาต์พุต stdout / ไฟล์ด้วยโปรแกรมที่รองรับเฉพาะโปรแกรมอื่นเท่านั้น


2
อันนี้ใช้ได้กับ pfSense (FreeBSD) ในขณะที่คำตอบที่ยอมรับไม่ได้ ขอขอบคุณ!
นาธาน

9
(tail -f /tmp/p1 & tail -f /tmp/p2 ) | cat > /tmp/output

/tmp/p1และ/tmp/p2เป็นไพพ์อินพุตของคุณในขณะ/tmp/outputที่เอาต์พุต


6
หมายเหตุ: หากทั้งสองคำสั่งด้านข้าง()ล้างข้อมูลออกของพวกเขาในทุกบรรทัด (และบางกฎ POSIX ที่คลุมเครือสำหรับอะตอมมิก) คุณอาจจบลงด้วยการตรวจสอบที่แปลก ๆ บางประการเกี่ยวกับอินพุตเพื่อ cat ...
คิดเห็น

คุณไม่ควรใช้เครื่องหมายอัฒภาคแทนอักขระเครื่องหมายแอมเปอร์แซนด์?
Samir

นี่คือสิ่งที่มหากาพย์
Mobigital

5

ฉันได้สร้างโปรแกรมพิเศษสำหรับสิ่งนี้: fdlinecombine

มันอ่านไปป์หลาย ๆ ตัว (โดยปกติจะเป็นเอาต์พุตของโปรแกรม) และเขียนไปยัง stdout linewise (คุณยังสามารถแทนที่ตัวคั่นได้)


ทำงานตามที่โฆษณาไว้ ขอบคุณที่ทำให้เป็นสาธารณะ
alexei

3

คำสั่งที่ยอดเยี่ยมจริงๆที่ฉันใช้สำหรับสิ่งนี้คือtpipeคุณอาจต้องรวบรวมเพราะมันไม่ธรรมดา มันยอดเยี่ยมมากสำหรับการทำสิ่งที่คุณพูดถึงและมันสะอาดมากฉันมักจะติดตั้งมัน หน้าคนจะอยู่ที่นี่http://linux.die.net/man/1/tpipe ดาวน์โหลดจดทะเบียนปัจจุบันอยู่ที่คลังนี้http://www.eurogaran.com/downloads/tpipe/

มันใช้แบบนี้

## Reinject sub-pipeline stdout into standard output:
$ pipeline1 | tpipe "pipeline2" | pipeline3

3

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

tail -f / tmp / p1 / tmp / p2> / tmp / output

จะทำงาน. หากไม่เป็นไรคุณจะต้องหาสิ่งที่จะทำการบัฟเฟอร์บรรทัดและส่งออกเฉพาะบรรทัดที่สมบูรณ์เท่านั้น Syslog ทำสิ่งนี้ แต่ฉันไม่แน่ใจว่าจะเกิดอะไรขึ้น

แก้ไข: การปรับให้เหมาะสมสำหรับการอ่านที่ไม่มีบัฟเฟอร์และการตั้งชื่อไพพ์:

พิจารณา / tmp / p1, / ​​tmp / p2, / tmp / p3 เป็นไพพ์ที่มีชื่อสร้างโดย "mkfifo / tmp / p N "

tail -q -f / tmp / p1 / tmp / p2 | awk '{พิมพ์ $ 0> "/ tmp / p3"; ปิด ( "/ tmp / p3"); fflush ();} '&

ตอนนี้ด้วยวิธีนี้เราสามารถอ่าน Output named pipe "/ tmp / p3" ได้ถูกทำให้ยุ่งเหยิงโดย:

tail -f / tmp / p3

มีข้อผิดพลาดเล็ก ๆ น้อย ๆ ของการจัดเรียงคุณต้อง "เริ่มต้น" ที่ 1 input pipe / tmp / p1 โดย:

echo -n> / tmp / p1

เพื่อท้ายจะยอมรับอินพุตจาก 2nd pipe / tmp / p2 ก่อนและไม่รอจนกว่าจะมีบางอย่างมาถึง / tmp / p1 นี่อาจเป็นกรณีนี้หากคุณแน่ใจว่า / tmp / p1 จะได้รับอินพุตก่อน

นอกจากนี้ยังจำเป็นต้องใช้ตัวเลือก -q เพื่อตัดหางไม่พิมพ์ขยะเกี่ยวกับชื่อไฟล์


มีประโยชน์มากขึ้นจะเป็น: "tail -q -f / tmp / p1 / tmp / p2 | another_command"เพราะมันจะทำทีละบรรทัดและด้วยตัวเลือก -q มันจะไม่พิมพ์ขยะอื่น ๆ
readyblue

สำหรับไฟล์ที่ไม่มีบัฟเฟอร์ / ชื่อไปป์ใช้: tail -q -f /tmp/p1 /tmp/p2 | awk '{print $0 > "/tmp/p3"; close("/tmp/p3"); fflush();}' & ตอนนี้ / tmp / p3 สามารถตั้งชื่อไปป์ได้และคุณสามารถอ่านได้โดยtail -f /tmp/p3ทั้งหมดนี้คือUNBUFFERED = ทีละบรรทัด แต่ข้อผิดพลาดเล็ก ๆ ของการจัดเรียง ไฟล์แรก / ชื่อไปป์จะต้องเริ่มต้นก่อนเพื่อหางจะยอมรับการส่งออกจาก 2 ดังนั้นคุณจะต้องecho -n > /tmp/p1และทุกสิ่งจะทำงานได้อย่างราบรื่น
readyblue

1

lmergeโปรแกรมที่ดีที่สุดสำหรับการทำเช่นนี้คือ ต่างจากคำตอบของ freihart มันเป็นแบบ line-oriented ดังนั้นเอาต์พุตของทั้งสองคำสั่งจะไม่รบกวนซึ่งกันและกัน แตกต่างจากโซลูชันอื่น ๆ โดยรวมการป้อนข้อมูลอย่างเป็นธรรมดังนั้นจึงไม่มีคำสั่งใดสามารถควบคุมเอาต์พุตได้ ตัวอย่างเช่น:

$ lmerge <(yes foo) <(yes bar) | head -n 4

ให้ผลลัพธ์จาก:

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