ฉันจะใช้ tee เพื่อเปลี่ยนเส้นทางไปยัง grep ได้อย่างไร


13

ฉันไม่ค่อยมีประสบการณ์ในการใช้เสื้อยืดดังนั้นฉันหวังว่านี่จะไม่ธรรมดา

หลังจากที่ได้ดูหนึ่งในคำตอบที่จะคำถามนี้ฉันมาข้าม beheviour teeแปลก

เพื่อให้ฉันออกบรรทัดแรกและบรรทัดที่พบฉันสามารถใช้สิ่งนี้:

ps aux | tee >(head -n1) | grep syslog
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4

อย่างไรก็ตามครั้งแรกที่ฉันรันสิ่งนี้ (เป็น zsh) ผลลัพธ์อยู่ในลำดับที่ไม่ถูกต้องส่วนหัวคอลัมน์จะต่ำกว่าผลลัพธ์ grep (นี่ไม่ได้เกิดขึ้นอีกแล้ว) ดังนั้นฉันจึงพยายามสลับคำสั่งรอบ ๆ :

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

พิมพ์บรรทัดแรกเท่านั้นและไม่มีอะไรอื่น! ฉันสามารถใช้ tee เพื่อเปลี่ยนเส้นทางไปยัง grep หรือฉันทำสิ่งนี้ในลักษณะที่ไม่ถูกต้องได้หรือไม่?

ขณะที่ฉันพิมพ์คำถามนี้คำสั่งที่สองใช้งานได้จริงสำหรับฉันฉันวิ่งไปอีกห้าครั้งแล้วกลับไปที่ผลลัพธ์หนึ่งบรรทัด นี่เป็นเพียงระบบของฉันหรือไม่ (ฉันกำลังเรียกใช้ zsh ภายใน tmux)

สุดท้ายทำไมด้วยคำสั่งแรกคือ "grep syslog" ไม่แสดงผล (มีเพียงผลเดียว)?

สำหรับการควบคุมนี่คือ grep ที่ไม่มี tee

ps aux | grep syslog
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4
henry    2290  0.0  0.1  95220  3092 ?        Ssl  Sep07   3:12 /usr/bin/pulseaudio --start --log-target=syslog
henry   15924  0.0  0.0   3128   824 pts/4    S+   13:44   0:00 grep syslog

อัปเดต: ดูเหมือนว่าส่วนหัวทำให้คำสั่งทั้งหมดถูกตัดทอน (ดังที่ระบุไว้ในคำตอบด้านล่าง) คำสั่งด้านล่างจะคืนค่าดังต่อไปนี้:

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806

ไม่ได้เป็นคำตอบที่ตรงกับคำถามของคุณ ps aux | sed -n -e '1p' -e '/syslog/p'แต่มันจะทำความสะอาดมากเพียงแค่ทำสิ่งที่ชอบ
jw013

ฉันไม่เคยนึกถึงความคิดฉันคิดว่านั่นอาจเป็นคำตอบที่เหมาะสมสำหรับคำถามที่เกี่ยวข้องที่นี่แต่ฉันกำลังมองหาข้อมูลเกี่ยวกับพฤติกรรมที่ไม่สอดคล้องกันของคำสั่งเหล่านี้!
Rqomey

คำตอบ:


19
$ ps aux | tee >(head -n1) | grep syslog
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND 
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4

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

  1. ข้อมูลจากมัลติเพล็กteeจริงได้รับส่งไปยังกระบวนการหนึ่งก่อนที่อื่น ๆ teeขึ้นอยู่เป็นหลักในการดำเนินการ การteeใช้งานอย่างง่ายจะreadป้อนข้อมูลจำนวนหนึ่งและจากนั้นwriteสองครั้ง: หนึ่งครั้งเพื่อ stdout และอีกครั้งกับอาร์กิวเมนต์ ซึ่งหมายความว่าหนึ่งในจุดหมายปลายทางเหล่านั้นจะได้รับข้อมูลก่อน

    อย่างไรก็ตามท่อถูกบัฟเฟอร์ทั้งหมด อาจเป็นไปได้ว่าบัฟเฟอร์เหล่านี้มี 1 บรรทัดในแต่ละบรรทัด แต่อาจมีขนาดใหญ่กว่าซึ่งอาจทำให้หนึ่งในคำสั่งที่ได้รับเพื่อดูทุกสิ่งที่ต้องการสำหรับเอาต์พุต (เช่นgrepบรรทัด ped) ก่อนคำสั่งอื่น ( head) ได้รับข้อมูลใด ๆ ทั้งหมด

  2. แม้ว่าข้างต้นจะเป็นไปได้ว่าคำสั่งอย่างใดอย่างหนึ่งเหล่านี้ได้รับข้อมูล แต่ไม่สามารถทำอะไรกับมันได้ทันเวลาจากนั้นคำสั่งอื่นจะรับข้อมูลและประมวลผลเพิ่มเติมอย่างรวดเร็ว

    ตัวอย่างเช่นแม้ว่าheadและgrepจะถูกส่งข้อมูลทีละหนึ่งบรรทัดหากheadไม่ทราบวิธีจัดการกับมัน (หรือล่าช้าโดยการตั้งเวลาเคอร์เนล) grepสามารถแสดงผลลัพธ์ก่อนที่headจะมีโอกาสได้ ในการสาธิตให้ลองเพิ่มการหน่วงเวลา: ps aux | tee >(sleep 1; head -n1) | grep syslogสิ่งนี้จะส่งgrepออกผลลัพธ์ก่อน

$ ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

ฉันเชื่อว่าคุณมักจะได้รับเพียงบรรทัดเดียวที่นี่เพราะheadได้รับบรรทัดแรกของการป้อนข้อมูลแล้วปิด stdin และออก เมื่อteeเห็นว่า stdout ถูกปิดมันจะปิด stdin ของตัวเอง (ออกจากps) และออก สิ่งนี้อาจขึ้นอยู่กับการนำไปใช้งาน

อย่างมีประสิทธิภาพข้อมูลเดียวที่psส่งได้คือบรรทัดแรก (แน่นอนเพราะheadควบคุมสิ่งนี้) และอาจมีบรรทัดอื่นก่อนhead& teeปิด stdin descriptors

ความไม่สอดคล้องกับว่าจะปรากฏบรรทัดที่สองโดยการจับเวลา: headปิด stdin แต่psยังคงส่งข้อมูลอยู่ เหตุการณ์ทั้งสองนี้ไม่ซิงโครไนซ์กันอย่างดีดังนั้นบรรทัดที่มีsyslogยังคงมีโอกาสที่จะทำให้มันteeเป็นอาร์กิวเมนต์ ( grepคำสั่ง) นี่คล้ายกับคำอธิบายข้างต้น

คุณสามารถหลีกเลี่ยงปัญหานี้ได้ทั้งหมดโดยใช้คำสั่งที่รออินพุตทั้งหมดก่อนปิด stdin / exiting ตัวอย่างเช่นใช้awkแทนheadซึ่งจะอ่านและประมวลผลทุกบรรทัดของมัน (แม้ว่าจะไม่ทำให้เกิดเอาต์พุต):

ps aux | tee >(grep syslog) | awk 'NR == 1'

แต่โปรดทราบว่าบรรทัดนั้นยังคงปรากฏออกมาไม่เรียบร้อยดังที่ได้แสดงให้เห็นโดย:

ps aux | tee >(grep syslog) | (sleep 1; awk 'NR == 1')

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


1
คำตอบที่ยอดเยี่ยม! ฉันถามจริงเพราะฉันสนใจในกระบวนการพื้นฐาน เมื่อสิ่งต่าง ๆ ไม่แน่นอนฉันพบว่ามันน่าสนใจ จะมีวิธีที่ดีกว่าในการทำงานps aux | tee >(grep syslog) | head -n1ซึ่งจะหยุดการheadปิด stdout ว้าวคำสั่งนี้ได้เริ่มให้ผลผลิตตอนนี้ แต่ตามที่เกิดขึ้นกับคำตอบของคุณดูเหมือนว่าจะถูกตัดทอนUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND syslog 806
Rqomey

1
คุณสามารถใช้สิ่งที่ไม่ได้ใกล้ stdin headแทน ฉันได้อัปเดตคำตอบด้วยตัวอย่างนี้:ps aux | tee >(grep syslog) | awk 'NR == 1'
mrb

1
@KrzysztofAdamski เมื่อคุณใช้>(cmd)เชลล์จะสร้างไพพ์ที่มีชื่อและส่งต่อให้เป็นอาร์กิวเมนต์ไปยังคำสั่ง ( tee) จากนั้นteeเขียนถึง stdout (piped to awk) และไปยังอาร์กิวเมนต์นั้น มันเหมือนกับmkfifo a_fifo ; grep ... a_fifoในเชลล์หนึ่งและps | tee a_fifo | awk ...อีกเชลล์
mrb

1
@KrzysztofAdamski gnu.org/software/bash/manual/html_node/ ...... - ลองecho >(exit 0)ซึ่งจะสะท้อนอาร์กิวเมนต์ที่เกิดขึ้นจริงผ่านเชลล์ (ในกรณีของฉันมันจะกลายเป็น/dev/fd/63) สิ่งนี้ควรใช้งานได้ดีกับ bash และ zsh
mrb

1
@mrb: มันเป็นคุณสมบัติที่น่าสนใจมากที่ฉันไม่เคยรู้มาก่อนขอบคุณ มันคือการทำงานในบางวิธีที่แปลกในทุบตี แต่ดูpastebin.com/xFgRcJdF น่าเสียดายที่ฉันไม่มีเวลาตรวจสอบในตอนนี้ แต่ฉันจะทำในวันพรุ่งนี้
Krzysztof Adamski

2

grep syslogไม่ได้แสดงเสมอเนื่องจากขึ้นอยู่กับเวลา เมื่อใช้เชลล์ไปป์ไลน์คุณกำลังเรียกใช้คำสั่งเกือบจะพร้อมกัน แต่สิ่งสำคัญที่นี่คือคำว่า "เกือบ" หากpsเสร็จสิ้นการสแกนกระบวนการทั้งหมดก่อนที่จะเปิด grep มันจะไม่อยู่ในรายการ คุณสามารถได้รับผลการสุ่มขึ้นอยู่กับภาระของระบบ ฯลฯ

สิ่งที่คล้ายกันเกิดขึ้นกับทีของคุณ มันทำงานบนพื้นหลังใน subshell และมันอาจถูกยิงก่อนหรือหลัง grep นี่คือเหตุผลที่ใบสั่งส่งออกไม่สอดคล้องกัน

สำหรับคำถามทีพฤติกรรมของมันค่อนข้างแปลก นี่เป็นเพราะมันไม่ได้ใช้ในวิธีปกติ มันทำงานโดยไม่มีข้อโต้แย้งใด ๆ ซึ่งหมายความว่ามันควรจะคัดลอกข้อมูลจาก stdin ไปยัง stdout แต่ stdout นั้นจะถูกเปลี่ยนเส้นทางไปยัง subshell running head (ในกรณีแรก) หรือ grep (ตัวพิมพ์ที่สอง) แต่มันจะถูกไพพ์ไปยังคำสั่งถัดไป ฉันคิดว่าสิ่งที่เกิดขึ้นในกรณีนี้ขึ้นอยู่กับการใช้งานจริง ตัวอย่างเช่นใน bash ของฉัน 4.2.28 ไม่มีสิ่งใดถูกเขียนไปยัง subshell stdin บน zsh มันทำงานได้อย่างน่าเชื่อถือในแบบที่คุณต้องการ (พิมพ์ทั้งบรรทัดแรกของ ps และค้นหาบรรทัด) ทุกครั้งที่ฉันลอง


นั่นอธิบายอย่างหนึ่งอยู่ดีฉันประหลาดใจที่ที grep ทำงานช้าลงจนเกินขอบเขต!
Rqomey

0

ค่อนข้างแฮ็ค แต่นี่เป็นวิธีแก้ปัญหาของฉันในรูปแบบของpsgrep()ฟังก์ชั่นเชลล์ที่ฉันใช้:

เปลี่ยนเส้นทางpsแถวส่วนหัวไปยังSTDERRจากนั้นgrepเปิดSTDOUTแต่ก่อนอื่นให้ลบgrepคำสั่งออกเพื่อหลีกเลี่ยงแถว "สัญญาณรบกวน" ที่เกิดจากgrepตัวเอง:

psgrep() { ps aux | tee >(head -1>&2) | grep -v " grep $@" | grep "$@" -i --color=auto; }
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.