วิธีการ stderr ท่อโดยไม่ต้องท่อ stdout


24

ฉันจะไพพ์สตรีมข้อผิดพลาดมาตรฐานโดยไม่ต้องไพพ์สตรีมออกมาตรฐานได้อย่างไร

ฉันรู้ว่าคำสั่งนี้ใช้งานได้ แต่มันก็เขียนมาตรฐาน

Command 2>&1 | tee -a $LOG

ฉันจะได้รับเพียงข้อผิดพลาดมาตรฐานได้อย่างไร

หมายเหตุ: สิ่งที่ฉันต้องการคือการเขียนสตรีม stderr ไปยังบันทึกและเขียนทั้ง stderr และ stdout ไปที่คอนโซล

คำตอบ:


26

หากต้องการทำเช่นนั้นให้ใช้ file descriptor หนึ่งไฟล์เพื่อสลับ stderr และ stdout:

find /var/log 3>&1 1>&2 2>&3 | tee foo.file

โดยพื้นฐานแล้วมันใช้งานได้หรืออย่างน้อยฉันคิดว่ามันใช้งานได้ดังต่อไปนี้
ทิศทางใหม่จะถูกประเมินจากซ้ายไปขวา

3>&1 สร้างไฟล์ descriptor ใหม่ 3 สำเนา (สำเนา) ของ fd 1 (stdout)

1>&2 ทำ stdout (1) ซ้ำกับ fd 2 (stderr)

2>&3 ทำ fd 2 ซึ่งเป็นสำเนา (สำเนา) จำนวน 3 ซึ่งทำสำเนาไว้ก่อนหน้านี้

ดังนั้นตอนนี้ stderr และ stdout จะเปลี่ยน

| tee foo.file tee file descriptor ที่ซ้ำกัน 1 ซึ่งถูกทำเป็น stderr


โอ้ไม่ได้ทดสอบกับ ksh ทำงานร่วมกับทุบตีแม้ว่า ...
ไคล์ Brandt

ขอบคุณทำงานเป็น ksh ด้วย ฉันคิดว่าส่วนใหญ่ของท่อและสิ่งที่สตรีมเป็นมาตรฐาน posix
C. Ross

"คัดลอก" ไม่ถูกต้อง - ดูคำตอบของ @ Guasqueño
Kyle Brandt

สิ่งนี้สามารถใช้งานได้ใน Windows ที่tee.exeติดตั้ง :)
Acorn

13

คำสั่ง Unix / Linux ของ Kyle ทำหน้าที่ในการสลับ STDERR ด้วย STDOUT; อย่างไรก็ตามคำอธิบายนั้นไม่ถูกต้องนัก ผู้ประกอบการเปลี่ยนเส้นทางไม่ทำการคัดลอกหรือทำซ้ำพวกเขาเพียงแค่เปลี่ยนเส้นทางไปยังทิศทางอื่น

เขียนใหม่คำสั่งของ Kyle โดยการเลื่อน 3> & 1 ไปยังจุดสิ้นสุดเป็นการชั่วคราวทำให้เข้าใจแนวคิดได้ง่ายขึ้น:

find /var/log  1>&2  2>&3  3>&1  

แม้ว่าจะเขียนด้วยวิธีนี้ Linux จะแสดงข้อผิดพลาดเนื่องจาก & 3 ยังไม่มีอยู่เนื่องจากอยู่ก่อน 3> & 1 3> บางอย่างเป็นวิธีที่จะประกาศ (define) ว่าเราจะใช้ท่อสามดังนั้นมันจะต้องอยู่ก่อนที่เราจะไหลน้ำลงในท่อนั้นตัวอย่างเช่นวิธีที่ไคล์เขียน ลองวิธีนี้เพื่อความสนุก:

((echo "STD1";  anyerror "bbbb"; echo "STD2" ) 3>&1 4>&2 1>&4 2>&3) > newSTDOUT 2> newSTDERR

การไม่มีวิธีทำสำเนาเป็นเรื่องที่น่าละอาย คุณไม่สามารถทำสิ่งต่าง ๆ เช่น "3> & 1 3> & 2" ในคำสั่งเดียวกันได้เนื่องจาก Linux จะใช้ตัวแรกที่พบและไม่สนใจตัวที่สอง

ฉันยังไม่พบวิธีส่งทั้งข้อผิดพลาดและเอาต์พุตปกติไปยังไฟล์และยังส่งสำเนาข้อผิดพลาดไปยังเอาต์พุตมาตรฐานด้วยคำสั่งเดียว สำหรับอินสแตนซ์ฉันมีงาน cron ที่ฉันต้องการให้ทั้งเอาต์พุต (ข้อผิดพลาดและมาตรฐาน) ไปที่ล็อกไฟล์และปล่อยให้ข้อผิดพลาดออกไปเพื่อทำให้ข้อความอีเมลถูกส่งไปยังแบล็กเบอร์รี่ของฉัน ฉันสามารถทำได้ด้วยสองคำสั่งโดยใช้ "tee" แต่ข้อผิดพลาดไม่แสดงในลำดับที่ถูกต้องในบรรทัดเอาต์พุตปกติในไฟล์ นี่เป็นวิธีที่น่าเกลียดที่ฉันแก้ไขปัญหา:

((echo "STD1"; sdfr "bbbb"; echo "STD2" ) 3>&1 1>&2 2>&3 | tee -a log1 ) 2>> log1

โปรดทราบว่าฉันต้องใช้ log1 สองครั้งและฉันต้องผนวกทั้งสองกรณีตัวแรกใช้ตัวเลือก "-a" สำหรับคำสั่ง "tee" และตัวที่สองใช้ ">>"

ทำcat log1คุณจะได้รับสิ่งต่อไปนี้:

STD1
STD2
-bash: sdfr: command not found

ขอให้สังเกตว่าข้อผิดพลาดไม่แสดงในบรรทัดที่สองตามที่ควร


การแก้ไขที่ยอดเยี่ยม !
Kyle Brandt

ตรวจสอบ zsh และmult_iosตัวเลือก (ตามค่าเริ่มต้น) เพื่อให้สามารถเปลี่ยนเส้นทาง FD ได้หลายครั้ง
Tom Hale

2

ตามหน้า man สำหรับ ksh (pdksh) คุณสามารถทำได้:

คำสั่ง 2> & 1> / dev / null | แมว -n

ie dup stderr ไปยัง stdout เปลี่ยนเส้นทาง stdout เป็น / dev / null จากนั้นไปที่ 'cat -n'

ทำงานบน pdksh ในระบบของฉัน:

$ errorecho () {echo "$ @"> & 2;}

$ errorecho foo
foo

$ errorecho foo> / dev / null # ควรยังคงแสดงแม้จะมีการเปลี่ยนเส้นทาง stdout
foo

$ errorecho foo 2> & 1> / dev / null | แมว -n
     1 foo
$   

ทำงานร่วมกับ BusyBox ด้วย
Udo G

1

ฉันได้มันทำงานเหมือนที่คุณต้องการตั้งแต่ฉันยังต้องการมันและปรับปรุงคำสั่งของคุณ ตอนนี้สำหรับฉันมันใช้งานได้ถูกต้องโดยใช้ bash 3.2 บนเดเบียนบีบที่ใช้สิ่งนี้

(echo "foo" 3>&1 1>&2 2>&3 | tee -a log1 ) 2>> log1 >> log2

ในขณะที่ log1 บันทึก stdout และ stderr และ log2 เพียงบันทึก stderr และไม่มีอะไรอื่นที่จะนำไปที่หน้าจอ

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