คำสั่ง piped ทำงานในลำดับใด


89

ฉันไม่เคยคิดเลยจริงๆว่าเชลล์รันคำสั่ง piped อย่างไร ฉันได้รับแจ้งเสมอว่า "stdout ของโปรแกรมหนึ่งถูกส่งไปยัง stdin ของโปรแกรมอื่น" เป็นวิธีคิดเกี่ยวกับท่อ ดังนั้นโดยธรรมชาติฉันคิดว่าในกรณีที่พูดว่า A | B, A จะทำงานก่อนจากนั้น B รับ stdout ของ A และใช้ stdout ของ A เป็นอินพุต

แต่ฉันสังเกตเห็นว่าเมื่อผู้คนค้นหากระบวนการเฉพาะใน ps พวกเขาต้องการรวม grep -v "grep" ที่ท้ายคำสั่งเพื่อให้แน่ใจว่า grep ไม่ปรากฏในผลลัพธ์สุดท้าย ซึ่งหมายความว่าในคำสั่ง ps aux | grep "bash" | grep -v "grep" ซึ่งหมายความว่า ps รู้ว่า grep กำลังทำงานอยู่ดังนั้นจึงอยู่ในเอาต์พุตของ ps แต่ถ้า ps ทำงานเสร็จก่อนที่เอาต์พุตจะถูกส่งไปยัง grep มันจะรู้ได้อย่างไรว่า grep นั้นทำงานอยู่?

flamingtoast@FTOAST-UBUNTU: ~$ ps | grep ".*"
PID TTY          TIME CMD
3773 pts/0    00:00:00 bash
3784 pts/0    00:00:00 ps
3785 pts/0    00:00:00 grep

ทำไมไม่ยอมรับคำตอบ?
törzsmókus

คำตอบ:


64

คำสั่ง Piped ทำงานพร้อมกัน เมื่อคุณเรียกใช้ps | grep …มันเป็นโชคของการดึง (หรือรายละเอียดของการทำงานของเชลล์ที่รวมกับตัวกำหนดตารางเวลาการปรับจูนลึกเข้าไปในลำไส้ของเคอร์เนล) ว่าจะเริ่มต้นpsหรือไม่ก็ตามgrepและในกรณีใด ๆ ดำเนินการพร้อมกัน

โดยทั่วไปจะใช้เพื่ออนุญาตให้โปรแกรมที่สองประมวลผลข้อมูลตามที่ออกมาจากโปรแกรมแรกก่อนที่โปรแกรมแรกจะเสร็จสิ้นการทำงาน ตัวอย่างเช่น

grep pattern very-large-file | tr a-z A-Z

เริ่มแสดงบรรทัดที่ตรงกันเป็นตัวพิมพ์ใหญ่แม้กระทั่งก่อนที่จะgrepเสร็จสิ้นการข้ามไฟล์ขนาดใหญ่

grep pattern very-large-file | head -n 1

แสดงบรรทัดที่ตรงกันแรกและอาจหยุดการประมวลผลได้ดีก่อนที่จะgrepอ่านไฟล์อินพุตเสร็จสิ้น

หากคุณอ่านบางแห่งที่โปรแกรม piped ทำงานตามลำดับให้หนีเอกสารนี้ โปรแกรมไปป์ทำงานพร้อมกันและมีเสมอ


7
และสิ่งที่เจ๋งเกี่ยวกับตัวอย่างนี้คือเมื่อหัวได้รับหนึ่งบรรทัดที่ต้องการมันจะยุติและเมื่อ grep สังเกตเห็นสิ่งนี้มันก็จะสิ้นสุดลงโดยไม่ต้องทำงานอะไรเพิ่มเติม
Joe

ฉันเดาว่ามีบัฟเฟอร์ IO บางชนิดเกี่ยวกับไพพ์ ... ฉันจะรู้ขนาดของมันเป็นไบต์ได้อย่างไร ฉันต้องการอ่านอะไรเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับเรื่องนี้ :)
n611x007

3
@naxa มีสองบัฟเฟอร์จริง ๆ มีบัฟเฟอร์stdioอยู่ภายในgrepโปรแกรมและมีบัฟเฟอร์ที่จัดการโดยเคอร์เนลในไพพ์ สำหรับอันหลังดูที่ท่อบัฟเฟอร์ใหญ่แค่ไหน?
Gilles

49

ลำดับการรันคำสั่งไม่สำคัญและไม่รับประกัน ออกจากกันรายละเอียดที่เป็นความลับของpipe(), fork(), dup()และexecve()เปลือกแรกสร้างท่อท่อสำหรับข้อมูลที่จะไหลระหว่างกระบวนการและจากนั้นจะสร้างกระบวนการมีปลายของท่อที่เชื่อมต่อกับพวกเขา กระบวนการแรกที่รันอาจบล็อกการรออินพุตจากกระบวนการที่สองหรือบล็อกการรอให้กระบวนการที่สองเริ่มอ่านข้อมูลจากไปป์ การรอเหล่านี้อาจยาวโดยพลการและไม่เป็นไร ไม่ว่าจะสั่งให้กระบวนการใดทำงานข้อมูลในที่สุดก็จะถูกถ่ายโอนและทุกอย่างทำงานได้


5
คำตอบที่ดี แต่ OP ดูเหมือนว่ากระบวนการทำงานตามลำดับ คุณอาจทำให้ชัดเจนขึ้นว่ากระบวนการทำงานพร้อมกันและท่อเหมือน ... ท่อระหว่างถังที่น้ำไหลผ่านทั้งหมดในเวลาเดียวกัน (โดยประมาณ)
Keith

ขอบคุณสำหรับการชี้แจง แหล่งที่มาที่ฉันอ่านมาทำให้ดูเหมือนว่าโปรแกรม piped จะทำงานเรียงตามลำดับ
action_potato

หากต้องการดูประสบการณ์ของกระบวนการที่เริ่มต้นในแบบบึกบึนลองใช้งาน 1,000 ครั้ง: echo -na> & 2 | echo b> & 2
Ole Tange

28

เสี่ยงที่จะถูกม้าตายตายความเข้าใจผิดดูเหมือนจะเป็นเช่นนั้น

    A | B

เทียบเท่ากับ

    A > temporary_file 
    B < temporary_file 
    rm temporary_file

แต่กลับมาเมื่อ Unix ถูกสร้างขึ้นและเด็ก ๆ ขี่ไดโนเสาร์ไปที่โรงเรียนดิสก์มีขนาดเล็กมากและเป็นเรื่องปกติที่คำสั่งที่ค่อนข้างใจดีจะใช้พื้นที่ว่างทั้งหมดในระบบไฟล์ หากBเป็นสิ่งที่ชอบ , ผลลัพธ์สุดท้ายของท่อที่อาจจะมากขนาดเล็กกว่าแฟ้มกลางที่ ดังนั้นท่อจึงได้รับการพัฒนาไม่ใช่เป็นชวเลขสำหรับรุ่น "รันAก่อนจากนั้นเรียกใช้Bพร้อมอินพุตจากโมเดลA " ของเอาต์พุต แต่เป็นวิธีสำหรับดำเนินการพร้อมกัน และกำจัดความจำเป็นในการจัดเก็บไฟล์ระดับกลาง บนดิสก์grep some_very_obscure_stringBA


2
นี่เป็นคำตอบว่าเพราะเหตุใดและด้วยเหตุนี้ฉันจึงได้รับคะแนน
Little Ancient Forest Kami

1

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

  1. หากไม่ใช่คำสั่งสุดท้ายในไปป์ให้สร้างไปป์ที่ไม่มีชื่อกับซ็อกเก็ตคู่
  2. ส้อม
  3. ใน child กำหนด stdin / stdout ใหม่ให้กับซ็อกเก็ตหากจำเป็น (สำหรับกระบวนการแรกใน pipe stdin ไม่ได้ถูกกำหนดใหม่เหมือนกันสำหรับกระบวนการสุดท้ายและ stdout ของเขา)
  4. ใน child EXEC คำสั่งที่ระบุพร้อมด้วยอาร์กิวเมนต์ที่กวาดโค้ดเชลล์ต้นฉบับออกไป แต่ปล่อยให้ซ็อกเก็ตเปิดทั้งหมด ID กระบวนการลูกจะไม่ถูกเปลี่ยนเพราะนี่เป็นกระบวนการลูกที่เหมือนกัน
  5. พร้อมกันกับเด็ก ๆ แต่ขนานกันภายใต้เปลือกหลักไปที่ขั้นตอนที่ 1

ระบบไม่รับประกันว่า exec จะถูกดำเนินการอย่างรวดเร็วและเริ่มต้นคำสั่งที่ระบุ มันเป็นอิสระต่อเชลล์ แต่เป็นระบบ นี้เป็นเพราะ:

ps auxww| grep ps | cat

เมื่อแสดงgrepและ / หรือpsคำสั่งและต่อไปในขณะนี้ ขึ้นอยู่กับว่าเคอร์เนลเริ่มต้นอย่างรวดเร็วจริงๆกระบวนการโดยใช้ฟังก์ชั่นระบบ exec


1
การดำเนินการพร้อมกันหมายความว่ากระบวนการสองกระบวนการขึ้นไปดำเนินการภายในกรอบเวลาเดียวกันโดยปกติจะมีการพึ่งพาระหว่างกัน การประมวลผลแบบขนานหมายความว่ากระบวนการสองกระบวนการขึ้นไปดำเนินการพร้อมกัน (เช่นในคอร์ CPU แยกกันในเวลาเดียวกัน) ขนานไม่เกี่ยวข้องกับคำถามที่ไม่เป็น "วิธีการที่รวดเร็ว" exec()จะถูกดำเนินการ แต่วิธีการexec()โทรและการทำงานของโปรแกรมในท่อจะบรรณนิทัศน์
Thomas Nyman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.