ไพพ์ไปยังหลายไฟล์ในเชลล์


29

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

JUNK
JUNK
JUNK
JUNK
A 1
JUNK
B 5
C 1
JUNK

ฉันสามารถเรียกใช้แอปพลิเคชันได้สามครั้งดังนี้:

./app | grep A > A.out
./app | grep B > B.out
./app | grep C > C.out

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

มีวิธีใดบ้างที่จะรวมการดำเนินการทั้งสามที่แสดงไว้ด้านบนในลักษณะที่ฉันจะต้องเรียกใช้แอปพลิเคชันเพียงครั้งเดียวและยังคงได้รับไฟล์เอาต์พุตสามไฟล์

คำตอบ:


78

หากคุณมีที

./app | tee >(grep A > A.out) >(grep B > B.out) >(grep C > C.out) > /dev/null

(จากที่นี่ )

( เกี่ยวกับการทดแทนกระบวนการ )


4
ยอดเยี่ยมสิ่งนี้อาจแสดงผลเป็น:./app | tee >(grep A > A.out) >(grep B > B.out) | grep C > C.out
evilsoup

7
ปัจจุบันคำตอบนี้เป็นคำตอบเดียวที่ถูกต้องเนื่องจากได้รับชื่อเดิมของ "pipe ไปยังหลายกระบวนการ"
acelent

3
+1 grepนี่คือคำตอบส่วนใหญ่โดยทั่วไปที่ใช้บังคับเพราะมันไม่ได้ขึ้นอยู่กับความจริงที่ว่าคำสั่งที่ระบุการกรองเป็น
ruakh

1
ฉันยอมรับว่านี่เป็นคำตอบที่ดีที่สุดสำหรับคำถามที่โพสต์และควรทำเครื่องหมายเช่นนั้น Parallelเป็นโซลูชันอื่น (ตามที่โพสต์) แต่เมื่อทำการเปรียบเทียบหมดเวลาแล้วตัวอย่างด้านบนมีประสิทธิภาพมากขึ้น หาก op เกี่ยวข้องกับการดำเนินการที่ใช้ cpu สูงอย่างมากเช่นการบีบอัดไฟล์หลาย ๆ ไฟล์หรือการแปลง mp3 หลาย ๆ ครั้งก็ไม่ต้องสงสัยเลยว่าโซลูชันแบบขนานควรจะมีประสิทธิภาพมากกว่า
AsymLabs

32

คุณสามารถใช้ได้ awk

./app | awk '/A/{ print > "A.out"}; /B/{ print > "B.out"}; /C/{ print > "C.out"}'

6
ชื่อคำถามคือท่อหลายกระบวนการคำตอบนี้เป็นเรื่องเกี่ยวกับ "ท่อ" (เยี่ยงอย่างโดย regex) ไปหลายไฟล์ เนื่องจากคำตอบนี้ได้รับการยอมรับชื่อของคำถามจึงควรเปลี่ยนไป
acelent

@PauloMadeira คุณพูดถูก คุณคิดว่าอะไรจะเป็นชื่อที่ดีกว่า
sj755

ฉันได้แนะนำการแก้ไขเล็ก ๆ "ไปยังไฟล์หลายไฟล์ในเปลือก" มันอยู่ระหว่างรอการแก้ไขตรวจสอบ ฉันคาดหวังว่าจะลบความคิดเห็นหากได้รับการยอมรับ
acelent

@PauloMadeira - ฉันเปลี่ยนชื่อแล้ว ไม่เห็นการแก้ไขของคุณ แต่คุณถูกต้องการใช้กระบวนการในชื่อไม่ถูกต้องหากนี่เป็นคำตอบที่ยอมรับได้
slm

17

คุณสามารถใช้ความสามารถในการจับคู่รูปแบบของเชลล์:

./app | while read line; do 
     [[ "$line" =~ A ]] && echo $line >> A.out; 
     [[ "$line" =~ B ]] && echo $line >> B.out; 
     [[ "$line" =~ C ]] && echo $line >> C.out; 
 done

หรือแม้กระทั่ง:

./app | while read line; do for foo in A B C; do 
     [[ "$line" =~ "$foo" ]] && echo $line >> "$foo".out; 
  done; done

วิธีที่ปลอดภัยกว่าที่สามารถจัดการแบ็กสแลชและบรรทัดที่ขึ้นต้นด้วย-:

./app | while IFS= read -r line; do for foo in A B C; do 
     [[ "$line" =~ "$foo" ]] && printf -- "$line\n" >> "$foo".out; 
  done; done

ในฐานะที่เป็น @StephaneChazelas ชี้ให้เห็นในความคิดเห็นนี้ไม่ได้มีประสิทธิภาพมาก ทางออกที่ดีที่สุดน่าจะเป็น@ AurélienOoms'


ที่ถือว่าการป้อนข้อมูลที่ไม่ได้มีเครื่องหมายหรือช่องว่างหรืออักขระตัวแทนหรือสายที่เริ่มต้นด้วย-n, -e... นอกจากนี้ยังจะเป็นชะมัดไม่มีประสิทธิภาพเท่าที่มันหมายถึงสายระบบหลายต่อบรรทัด (อย่างใดอย่างหนึ่งread(2)ต่อตัวอักษร, ไฟล์ถูกเปิดการเขียน ปิดสำหรับแต่ละบรรทัด ... ) โดยทั่วไปแล้วการใช้while readลูปในการประมวลผลข้อความใน shells เป็นแนวปฏิบัติที่ไม่ดี
Stéphane Chazelas

@StephaneChazelas ฉันแก้ไขคำตอบของฉัน มันควรจะทำงานกับแบ็กสแลชและ-nอื่น ๆ ในตอนนี้ เท่าที่ฉันสามารถบอกได้ว่าทั้งสองเวอร์ชันใช้ได้กับช่องว่างฉันผิดหรือเปล่า
terdon

ไม่อาร์กิวเมนต์แรกprintfของรูปแบบคือ ไม่มีเหตุผลที่ทำให้คุณไม่สามารถระบุตัวแปรได้
Stéphane Chazelas

สิ่งนี้จะแตกในทุบตี (และกระสุนอื่น ๆ ที่ใช้ cstrings ด้วยวิธีที่คล้ายกัน) หากมีค่า null ในอินพุต
Chris Down

9

หากคุณมีหลายคอร์และคุณต้องการให้โพรเซสนั้นขนานกันคุณสามารถทำได้:

parallel -j 3 -- './app | grep A > A.out' './app | grep B > B.out' './app | grep C > C.out'

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

ยูทิลิตี้ GNU ขนานจากโอเล Tange สามารถได้รับจาก Repos มากที่สุดภายใต้ชื่อขนานหรือmoreutils แหล่งที่สามารถหาได้จากSavannah.gnu.org นอกจากนี้ยังมีวิดีโอการเรียนการสอนเบื้องต้นมีที่นี่

ภาคผนวก

เมื่อใช้รุ่นขนานรุ่นล่าสุด (ไม่จำเป็นต้องเป็นรุ่นใน repo การแจกจ่ายของคุณ) คุณสามารถใช้โครงสร้างที่หรูหรากว่าได้:

./app | parallel -j3 -k --pipe 'grep {1} >> {1}.log' ::: 'A' 'B' 'C'

ซึ่งบรรลุผลลัพธ์ของการรันหนึ่งกระบวนการ. / app และ grep แบบขนาน 3 กระบวนการในแกนหรือเธรดแยกกัน (ตามที่กำหนดโดยตัวมันเองแบบขนานก็ถือว่า -j3 เป็นทางเลือก แต่ให้มาในตัวอย่างนี้

สามารถรับเวอร์ชั่นขนานที่ใหม่กว่าได้โดยทำดังนี้

wget http://ftpmirror.gnu.org/parallel/parallel-20131022.tar.bz2

จากนั้นให้แกะกล่องซีดีตามปกติ - {date}, ./configure && make, sudo ทำการติดตั้ง สิ่งนี้จะติดตั้งแบบขนานหน้าคนแบบขนานและหน้าคนแบบขนาน


7

นี่คือหนึ่งใน Perl:

./app | perl -ne 'BEGIN {open(FDA, ">A.out") and 
                         open(FDB, ">B.out") and 
                         open(FDC, ">C.out") or die("Cannot open files: $!\n")} 
                  print FDA $_ if /A/; print FDB $_ if /B/; print FDC $_ if /C/'

1
sed -ne/A/w\ A.out -e/B/w\ B.out -e/C/p <in >C.out

... หาก<inสามารถอ่านได้ทั้งสาม outfiles จะถูกตัดทอนก่อนที่จะมีการเขียนอะไรถึงพวกเขา

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