เพราะเหตุใด '>' เปลี่ยนเส้นทางข้อความผิดพลาดจาก gcc


9

ฉันเก็บโปรแกรมต่อไปนี้ใน new.c

int main() 
{ 
    a;
    return 0; 
}

ส่งคืนข้อความแสดงข้อผิดพลาด ฉันต้องการส่งข้อความนี้ไปยังไฟล์ ดังนั้นฉันใช้คำสั่งดังต่อไปนี้

gcc new.c > temp.txt

แต่ยังฉันได้รับผลลัพธ์ใน terminal ฉันใช้ Ubuntu 13.04 ฉันจะทำให้มันทำงานได้อย่างไร


คำตอบ:


16

เมื่อคุณรวบรวมโปรแกรมที่มีgccมีชนิดที่แตกต่างกันของการส่งออก: ไปและstdout stderrโดยปกติ>จะส่งstdoutกระแสข้อมูลโดยตรงไปยังไฟล์ (ตัวอย่างเช่นผลลัพธ์ของ a printf("hello world\n");ถูกส่งไปยังstdout) อย่างไรก็ตามการstderrส่งต่อไปยังหน้าจอจะยังคงดำเนินต่อไปเนื่องจากจะถือว่าเป็น "บางสิ่งที่พิเศษที่คุณต้องบอกเกี่ยวกับ"

มีวิธีการเปลี่ยนเส้นทาง stderr ไปยังไฟล์ - คุณทำเช่นนี้ด้วยคำสั่ง (ไม่ง่ายมาก):

gcc new.c &> myFile

โดยที่&>"bash shorthand" สำหรับ "redirect ทุกอย่าง" ตามที่ถูกระบุโดย @CharlesDuffy รูปแบบที่สอดคล้องกับ POSIX คือ

gcc new.c > myFile 2>&1

ซึ่งหมายถึง "คอมไพล์ 'new.c' และส่งstdoutไปยังmyFileและส่งstderr(2) ไปยังสถานที่เดียวกับstdout( &1=" สถานที่เดียวกันกับ stdout ")

คุณจะพบรายละเอียดเพิ่มเติมเกี่ยวกับการเปลี่ยนเส้นทางที่แตกต่างกันที่http://tldp.org/HOWTO/Bash-Prog-Intro-HOWTO-3.htmlและhttp://mywiki.wooledge.org/BashFAQ/055

โดยวิธีการถ้าคุณต้องการส่งบางสิ่งจากภายในโปรแกรมของคุณโดยเฉพาะstderrคุณสามารถทำได้โดยทำสิ่งต่อไปนี้

fprintf(stderr, "hello world - this is urgent.\n");

หากคุณรวมสิ่งนั้นไว้ในโปรแกรมให้รันโปรแกรมและส่งเอาต์พุต "ปกติ" ไปยังไฟล์สิ่งนี้จะยังคงปรากฏบนคอนโซล ดังนั้นหากคุณรวบรวมข้างต้นลงในปฏิบัติการurgentแล้วพิมพ์

./urgent > /dev/null

ที่คอนโซลเอาต์พุตของคุณจะปรากฏบนหน้าจอ


2
mywiki.wooledge.org/BashFAQ/055น่าจะเป็นการแนะนำที่ดีกว่าสำหรับการเปลี่ยนเส้นทาง นอกจากนี้ควรนำเสนอแบบฟอร์ม POSIX-compliant ( >myFile 2>&1) เช่นเดียวกับส่วนขยาย bash ( &>)
ชาร์ลส์ดัฟฟี่

@CharlesDuffy - ทั้งคะแนนดีมาก ฉันจะรวมไว้ในคำตอบเพื่อความสมบูรณ์
Floris

11

เนื่องจากการ>เปลี่ยนเส้นทาง stdout เท่านั้นและมีการเขียนข้อผิดพลาดstderrคุณจึงต้องใช้วิธีใดวิธีหนึ่งต่อไปนี้แทน:

gcc new.c &> temp.txt ## redirect both stdout and stderr using bash or zsh only

...หรือ...

gcc new.c >temp.txt 2>&1 ## redirect both stdout and stderr in any POSIX shell

&>เป็นนามสกุล BASH ซึ่งเปลี่ยนเส้นทางทั้งstdoutและstderrไปยังไฟล์; วิธีที่ง่ายที่สุดคือการเป็นอย่างอื่น stdout เปลี่ยนเส้นทางแรก ( >temp.txt) แล้วทำให้ stderr (FD 2) สำเนาของการจัดการไฟล์แล้วเปลี่ยนเส้นทางบน stdout A (FD 1) 2>&1เช่นดังนั้น:


4

ดังที่คนอื่น ๆ ได้กล่าวไว้ linux จัดเตรียมสตรีมเอาต์พุตที่แตกต่างกันสองรายการ:

stdoutหรือ "เอาต์พุตมาตรฐาน" คือตำแหน่งที่เอาต์พุตปกติทั้งหมดไป คุณสามารถอ้างอิงได้โดยใช้อธิบายไฟล์
              1

stderrหรือ "ข้อผิดพลาดมาตรฐาน" เป็นสตรีมแยกต่างหากสำหรับข้อมูลนอกวง คุณสามารถอ้างอิงได้โดยใช้อธิบายไฟล์
              2

ทำไมต้องเลือกสตรีมเอาท์พุทสองแบบ พิจารณาขั้นตอนของคำสั่งจินตภาพ:

 decrypt $MY_FILE | grep "secret" | sort > secrets.txt

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

อย่างไรก็ตามตั้งแต่จับท่อเท่านั้นstdoutที่decryptคำสั่งสามารถส่งข้อผิดพลาดในการstderrที่พวกเขาจะได้รับการแสดงบนคอนโซล

คุณสามารถเปลี่ยนเส้นทางstdoutและstderrเข้าด้วยกันหรือเป็นอิสระ:

# Send errors to "errors.txt" and output to "secrets.txt"
# The following two lines are equivalent, as ">" means "1>"
decrypt $MY_FILE 2> errors.txt > secrets.txt
decrypt $MY_FILE 2> errors.txt 1> secrets.txt

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

# The operation "2>&1" means "redirect file descriptor 2 to file
# descriptor 1. So this sends all output from stderr to stdout.
# Note that the order of redirection is important.
decrypt $MY_FILE > errors.txt 2>&1 

# This may be confusing.  It will store the normal output in a file
# and send error messages to stdout, where they'll be captured by 
# the pipe and then sorted.
decrypt $MY_FILE 2>&1 > output.txt | sort

คุณยังสามารถใช้รูปแบบ "shorthand" เพื่อเปลี่ยนเส้นทางทั้ง stdout และ stderr ไปยังไฟล์เดียวกัน:

decrypt $MY_FILE &> output.txt

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

decrypt $MY_FILE 2>> more_errors.txt >> more_secrets.txt
decrypt $MY_FILE >> more_output.txt 2>&1

1
สอง quibbles: (1) การใช้การขยายพารามิเตอร์ unquoted ( $FOO) เป็นแหล่งที่มาของข้อบกพร่องและการสาธิตมันในตัวอย่างนั้นไม่ค่อยดีนัก (2) การใช้ชื่อตัวแปรตัวพิมพ์ใหญ่ทั้งหมดเป็นเหตุผลหลักสำหรับความขัดแย้งของเนมสเปซระหว่างสภาพแวดล้อมและตัวแปรในตัว (ตัวพิมพ์ใหญ่โดยการประชุม) และตัวแปรท้องถิ่น (ตัวพิมพ์เล็กตามแบบแผน) (3) กระตุ้นให้ผู้คนใช้ซ้ำ ๆ>>(ซึ่งจะเปิดไฟล์ใหม่ทุกครั้งที่มันถูกใช้ในคำสั่ง) แทนที่จะเปิดไฟล์หนึ่งครั้งและปล่อยให้ตัวบ่งชี้ไฟล์เปิดไว้เพื่อใช้งานโดยหลายคำสั่งทำให้รหัสไม่มีประสิทธิภาพ
ชาร์ลส์ดัฟฟี่

... ในจุดสุดท้ายเปรียบเทียบกับ: exec 4>secrets; echo "this is a secret" >&4; echo "this is another secret" >&4
Charles Duffy

+1 ขอบคุณที่ทำให้ฉันซื่อสัตย์ @CharlesDuffy ทุกจุดที่ดี ฉันตั้งใจละเว้นexecความเรียบง่าย แต่โดยทั่วไปแล้วมันเป็นกลยุทธ์ที่ดีกว่า

นอกจากนี้ยังสามารถยุบไปที่หรือ(ซึ่งจะต้องมีช่องว่างก่อนและหลังและและก่อน) command₁ > output_file ; command₂ >> the_same_output_file( command₁ ; command₂ )  > output_file{ command₁ ; command₂ ;  }  > output_file{;}
G-Man กล่าวว่า 'Reinstate Monica'
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.