วิธีการจับภาพสั่ง STDOUT / STDERR และเพิ่มการประทับเวลา / คำนำหน้า?


25

ฉันได้สำรวจคำถามที่คล้ายกันเกือบทั้งหมดที่ มีอยู่ แล้วเพื่อประโยชน์

ให้ฉันอธิบายปัญหาโดยละเอียด:

ฉันรันสคริปต์ที่ไม่ต้องใส่ข้อมูลบางส่วนและสิ่งเหล่านี้สามารถสร้างเอาต์พุตมาตรฐานและบรรทัดข้อผิดพลาดมาตรฐานได้ฉันต้องการจับภาพพวกเขาตามลำดับที่ถูกต้องตามที่แสดงโดยเทอร์มินัลอีมูเลเตอร์แล้วเพิ่มคำนำหน้าเช่น "STDERR:" และ "STDOUT:"

ฉันได้ลองใช้ไพพ์และแม้กระทั่งวิธีที่อิงจากพวกมันเพื่อประโยชน์แล้ว ฉันคิดว่าวิธีการแก้ปัญหาคือการใช้ pty แม้ว่าฉันจะไม่มีความเชี่ยวชาญในเรื่องนั้น ฉันได้แอบมองเข้าไปในซอร์สโค้ดของVTE ของ Gnomeแต่ก็ไม่ได้มีประโยชน์อะไรมาก

เป็นการดีที่ฉันจะใช้Goแทน Bash เพื่อทำสิ่งนี้ แต่ฉันไม่สามารถทำได้ ดูเหมือนว่าท่อโดยอัตโนมัติห้ามการสั่งซื้อบรรทัดที่ถูกต้องเพราะบัฟเฟอร์

มีใครบางคนที่สามารถทำสิ่งที่คล้ายกันได้หรือไม่? หรือเป็นไปไม่ได้เลยเหรอ? ฉันคิดว่าถ้าเทอร์มินัลอีมูเลเตอร์สามารถทำได้มันอาจไม่ใช่ - โดยการสร้างโปรแกรม C ขนาดเล็กที่จัดการ PTY แตกต่างกันหรือไม่

นึกคิดฉันจะใช้อินพุตแบบอะซิงโครนัสเพื่ออ่านสตรีม 2 รายการ (STDOUT และ STDERR) จากนั้นพิมพ์อีกครั้งตามความต้องการของฉัน แต่ลำดับของอินพุตเป็นสิ่งสำคัญ!

หมายเหตุ:ฉันทราบถึงstderredแต่มันไม่ได้ผลสำหรับฉันกับสคริปต์ Bash และไม่สามารถแก้ไขเพื่อเพิ่มคำนำหน้าได้อย่างง่ายดาย (เนื่องจากโดยทั่วไปแล้วจะมี syscalls มากมาย)

อัปเดต:เพิ่มด้านล่างสองส่วน

(สามารถเพิ่มการหน่วงเวลาย่อยวินาทีที่สองในสคริปต์ตัวอย่างที่ฉันให้ไว้เพื่อพิสูจน์ผลลัพธ์ที่สอดคล้องกัน)

อัปเดต:วิธีแก้ปัญหาสำหรับคำถามนี้จะช่วยแก้ปัญหาอื่น ๆตามที่ @Gilles ระบุ อย่างไรก็ตามฉันได้ข้อสรุปว่ามันเป็นไปไม่ได้ที่จะทำสิ่งที่ถามที่นี่และที่นั่น เมื่อใช้2>&1ทั้งสองกระแสจะถูกรวมอย่างถูกต้องที่ระดับ pty / pipe แต่การใช้กระแสแยกต่างหากและในลำดับที่ถูกต้องอย่างใดอย่างหนึ่งควรใช้วิธีการของstderredที่ involes syscall hooking และสามารถมองเห็นว่าสกปรกในหลาย ๆ

ฉันจะกระตือรือร้นที่จะอัปเดตคำถามนี้หากใครบางคนสามารถปิดการใช้งานดังกล่าวข้างต้น


1
นี่ไม่ใช่สิ่งที่คุณต้องการ? stackoverflow.com/questions/21564/…
slm

@slm อาจจะไม่ได้เนื่องจากความต้องการของ OP ต้องเพิ่มที่แตกต่างกันสตริงลำธารที่แตกต่างกัน
peterph

คุณสามารถแบ่งปันว่าทำไมการสั่งซื้อจึงมีความสำคัญมาก? บางทีอาจจะมีบางวิธีอื่น ๆ ปัญหาของคุณ ...
peterph

@ peterph มันเป็นสิ่งที่จำเป็นถ้าฉันไม่สามารถมีผลลัพธ์ที่สอดคล้องกันฉันอยากจะส่งไปยัง / dev / null กว่าอ่านมันและสับสนโดย :) 2> & 1 เก็บรักษาลำดับเช่น แต่ไม่อนุญาต ของการปรับแต่งที่ผมถามในคำถามนี้
Deim0s

1
ดูเพิ่มเติมที่groups.google.com/forum/#!topic/comp.unix.shell/zpAjBAO0pIo
peterph

คำตอบ:


12

คุณอาจใช้ตัวประมวลผลร่วม ตัวห่อหุ้มอย่างง่ายที่ดึงข้อมูลทั้งสองเอาต์พุตของคำสั่งที่กำหนดไปยังสองsedอินสแตนซ์ (อันหนึ่งสำหรับstderrอันอื่นสำหรับstdout) ซึ่งทำการแท็ก

#!/bin/bash
exec 3>&1
coproc SEDo ( sed "s/^/STDOUT: /" >&3 )
exec 4>&2-
coproc SEDe ( sed "s/^/STDERR: /" >&4 )
eval $@ 2>&${SEDe[1]} 1>&${SEDo[1]}
eval exec "${SEDo[1]}>&-"
eval exec "${SEDe[1]}>&-"

หมายเหตุหลายสิ่ง:

  1. มันเป็นคาถาวิเศษสำหรับคนจำนวนมาก (รวมถึงฉัน) - ด้วยเหตุผล (ดูคำตอบที่เชื่อมโยงด้านล่าง)

  2. ไม่มีการรับประกันว่าจะไม่สลับสองบรรทัดเป็นครั้งคราวทุกอย่างขึ้นอยู่กับการตั้งเวลาของตัวประมวลผลร่วม ที่จริงแล้วมันเกือบจะรับประกันได้ว่าในบางเวลามันจะ ที่กล่าวว่าหากการรักษาคำสั่งซื้ออย่างเคร่งครัดเหมือนกันคุณจะต้องประมวลผลข้อมูลจากทั้งสองstderrและstdinในกระบวนการเดียวกันมิฉะนั้นตัวจัดตารางเวลาเคอร์เนลสามารถ (และจะ) ทำให้เป็นระเบียบได้

    หากฉันเข้าใจปัญหาอย่างถูกต้องหมายความว่าคุณจะต้องสั่งให้เชลล์เปลี่ยนเส้นทางทั้งสองไปยังกระบวนการเดียว (ซึ่งสามารถทำได้ AFAIK) ปัญหาเริ่มต้นเมื่อกระบวนการนั้นเริ่มตัดสินใจว่าจะทำอะไรก่อน - จะต้องสำรวจทั้งแหล่งข้อมูลและในบางจุดเข้าสู่สถานะที่จะประมวลผลสตรีมหนึ่งและข้อมูลมาถึงสตรีมทั้งสองก่อนที่จะเสร็จสิ้น และนั่นคือสิ่งที่มันทำลายลง นอกจากนี้ยังหมายถึงการห่อหุ้มเอาต์พุต syscalls stderredนั้นน่าจะเป็นวิธีเดียวที่จะทำให้ได้ผลลัพธ์ตามที่คุณต้องการ (และถึงแม้คุณจะมีปัญหาก็ตาม

ตราบใดที่โปรเซสร่วมต้องแน่ใจว่าได้อ่านคำตอบที่ยอดเยี่ยมของStéphaneในคุณจะใช้คำสั่ง coproc ใน Bash ได้อย่างไร? สำหรับในเชิงลึก


ขอบคุณ @peterph สำหรับคำตอบของคุณอย่างไรก็ตามฉันกำลังมองหาวิธีที่จะรักษาลำดับไว้โดยเฉพาะ หมายเหตุ: ฉันคิดว่าล่ามของคุณควรจะถูกทุบตีเนื่องจากการทดแทนกระบวนการที่คุณใช้ (ฉันได้รับ./test1.sh: 3: ./test1.sh: Syntax error: "(" unexpectedโดยการคัดลอก / วางสคริปต์ของคุณ)
Deim0s

มีโอกาสมากฉันวิ่งเข้าไปbashด้วย/bin/sh(ไม่แน่ใจว่าทำไมฉันถึงมีอยู่)
เตอร์

ฉันได้อัปเดตคำถามเล็กน้อยเกี่ยวกับสถานที่ที่กระแสผสมอาจเกิดขึ้นได้
เตอร์

1
eval $@ค่อนข้างบั๊กกี้ ใช้"$@"หากคุณต้องการเรียกใช้อาร์กิวเมนต์ของคุณเป็นบรรทัดคำสั่งที่แน่นอน - การเพิ่มเลเยอร์ของevalการตีความเป็นการคาดเดาที่ยาก (และอาจเป็นอันตรายได้หากคุณผ่านชื่อไฟล์หรือเนื้อหาอื่น ๆ ที่คุณไม่สามารถควบคุมได้ ข้อโต้แย้ง) พฤติกรรมและไม่สามารถอ้างอิงแม้แต่ moreso (แยกชื่อด้วยช่องว่างเป็นหลายคำขยาย globs แม้ว่าพวกเขาจะยกมาก่อนหน้านี้เป็นตัวอักษร ฯลฯ )
Charles Duffy

1
นอกจากนี้ใน bash ที่ทันสมัยพอที่จะมีตัวประมวลผลร่วมคุณไม่จำเป็นต้อง evalปิดตัวให้คำอธิบายไฟล์ที่มีชื่อในตัวแปร exec {SEDo[1]}>&-จะทำงานตามที่เป็นอยู่ (ใช่ขาด$ก่อนที่จะ{พิจารณา)
Charles Duffy

5

วิธีที่ # 1 การใช้ file descriptors และ awk

สิ่งนี้เกี่ยวกับสิ่งนี้โดยใช้วิธีแก้ปัญหาจากคำถามและคำตอบ SO: มียูทิลิตี้ Unix ในการเพิ่มการประทับเวลาไปยังบรรทัดข้อความหรือไม่? และคำถามและคำตอบ SO นี้: pipe STDOUT และ STDERR ไปยังสองกระบวนการที่แตกต่างกันในเชลล์สคริปต์? .

วิธีการ

ขั้นตอนที่ 1 เราสร้าง 2 ฟังก์ชั่นใน Bash ที่จะดำเนินการกับข้อความประทับเวลาเมื่อเรียกว่า:

$ msgOut () {  awk '{ print strftime("STDOUT: %Y-%m-%d %H:%M:%S"), $0; fflush(); }'; }
$ msgErr () {  awk '{ print strftime("STDERR: %Y-%m-%d %H:%M:%S"), $0; fflush(); }'; }

ขั้นตอนที่ 2 คุณจะใช้ฟังก์ชั่นด้านบนเพื่อรับข้อความที่ต้องการ:

$ { { { ...command/script... } 2>&3; } 2>&3 | msgErr; } 3>&1 1>&2 | msgOut

ตัวอย่าง

ที่นี่ฉันได้สร้างตัวอย่างที่จะเขียนaไปยัง STDOUT พักเป็นเวลา 10 วินาทีแล้วเขียนผลลัพธ์ไปยัง STDERR เมื่อเราใส่ลำดับคำสั่งนี้ในโครงสร้างของเราด้านบนเราจะได้รับข้อความตามที่คุณระบุ

$ { { echo a; sleep 10; echo >&2 b; } 2>&3 | \
    msgErr; } 3>&1 1>&2 | msgOut
STDERR: 2014-09-26 09:22:12 a
STDOUT: 2014-09-26 09:22:22 b

วิธีที่ # 2 การใช้คำอธิบายประกอบ - เอาท์พุท

มีเครื่องมือที่เรียกannotate-outputว่าเป็นส่วนหนึ่งของdevscriptsแพ็คเกจที่จะทำสิ่งที่คุณต้องการ มีข้อ จำกัด เพียงอย่างเดียวคือจะต้องเรียกใช้สคริปต์สำหรับคุณ

ตัวอย่าง

หากเราใส่ลำดับคำสั่งตัวอย่างข้างต้นลงในสคริปต์ที่เรียกว่าmycmds.bashดังนี้:

$ cat mycmds.bash 
#!/bin/bash

echo a
sleep 10
echo >&2 b

จากนั้นเราสามารถเรียกใช้ดังนี้:

$ annotate-output ./mycmds.bash 
09:48:00 I: Started ./mycmds.bash
09:48:00 O: a
09:48:10 E: b
09:48:10 I: Finished with exitcode 0

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


1
น่าเสียดายที่นี่ยังไม่สามารถแก้ปัญหาการสลับบางบรรทัดได้
เตอร์

อย่างแน่นอน ฉันคิดว่าคำตอบสำหรับคำถามของฉันคือ "เป็นไปไม่ได้" กิจกรรมกับstderredคุณไม่สามารถกำหนดขอบเขตของเส้นได้อย่างง่ายดาย ผมอยากจะดูว่าใครสักคนที่จะช่วยฉันกับปัญหานี้ แต่เห็นได้ชัดว่าทุกคนต้องการที่จะให้ขึ้นข้อ จำกัด เดียว ( เพื่อ ) ที่เป็นพื้นฐานสำหรับคำถาม
Deim0s

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