ความแตกต่างระหว่าง 2> & 1> output.log และ 2> & 1 | ออกที. ล็อก


35

ฉันต้องการทราบความแตกต่างระหว่างสองคำสั่งต่อไปนี้

2>&1 > output.log 

และ

2>&1 | tee output.log

ฉันเห็นเพื่อนร่วมงานคนหนึ่งของฉันใช้ตัวเลือกที่สองเพื่อเปลี่ยนเส้นทาง ฉันรู้ว่า 2> & 1 ทำอะไรคำถามเดียวของฉันคือวัตถุประสงค์ของการใช้ทีที่ตัวดำเนินการเปลี่ยนเส้นทางแบบง่าย ">" สามารถใช้งานได้อย่างไร

คำตอบ:


11

ดูสองคำสั่งแยกจากกัน:

utility 2>&1 >output.log 

ที่นี่เนื่องจากการเปลี่ยนเส้นทางได้รับการประมวลผลในลักษณะจากซ้ายไปขวาสตรีมข้อผิดพลาดมาตรฐานจะถูกเปลี่ยนเส้นทางไปที่ใดก็ตามที่สตรีมเอาต์พุตมาตรฐานไปที่ (อาจเป็นคอนโซล) จากนั้นสตรีมเอาต์พุตมาตรฐานจะถูกเปลี่ยนเส้นทางไปยังไฟล์ สตรีมข้อผิดพลาดมาตรฐานจะไม่ถูกเปลี่ยนเส้นทางไปยังไฟล์นั้น

ผลกระทบที่มองเห็นได้จากสิ่งนี้คือคุณได้รับสิ่งที่เกิดจากข้อผิดพลาดมาตรฐานบนหน้าจอและสิ่งที่สร้างจากเอาต์พุตมาตรฐานในไฟล์

utility 2>&1 | tee output.log

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

สิ่งใดที่เคยมีใช้มาขึ้นอยู่กับสิ่งที่คุณต้องการจะทำให้สำเร็จ

โปรดทราบว่าคุณจะไม่สามารถสร้างเอฟเฟ็กต์ของไพพ์ไลน์ที่สองด้วยเพียง>(เหมือนutility >output.log 2>&1ซึ่งจะบันทึกทั้งเอาต์พุตมาตรฐานและข้อผิดพลาดในไฟล์) คุณจะต้องใช้teeเพื่อรับข้อมูลในคอนโซลเช่นเดียวกับในไฟล์เอาต์พุต


หมายเหตุเพิ่มเติม:

มองเห็นผลกระทบจากคำสั่งแรก

utility 2>&1 >output.log 

จะเป็นเช่นเดียวกับ

utility >output.log

คือออกมาตรฐานไปที่ไฟล์และข้อผิดพลาดมาตรฐานไปที่คอนโซล

หากมีการเพิ่มขั้นตอนการประมวลผลเพิ่มเติมในตอนท้ายของคำสั่งข้างต้นแต่ละคำสั่งจะมีความแตกต่างใหญ่:

utility 2>&1 >output.log | more_stuff

utility >output.log      | more_stuff

ในไพพ์ไลน์แรกmore_stuffจะได้รับสิ่งที่เดิมเป็นสตรีมข้อผิดพลาดมาตรฐานจากutilityเป็นข้อมูลอินพุตมาตรฐานในขณะที่ไพพ์ไลน์ที่สองเนื่องจากเป็นเพียงสตรีมเอาต์พุตมาตรฐานที่เป็นผลลัพธ์ที่เคยส่งผ่านข้ามไพพ์more_stuffส่วนของไพพ์ไลน์จะไม่ได้รับอะไรเลย เพื่ออ่านบนอินพุตมาตรฐาน


ด้วยคำสั่ง " utility 2>&1 | tee output.logคุณหมายถึงว่าตั้งแต่ 1 กำลังถูกนำไปยัง tee, 2 เช่นกันเนื่องจาก tee ซ้ำกับสตรีมเอาต์พุตจะแสดงทั้งบนคอนโซลและเขียนลงในไฟล์ดังนั้นความแตกต่างระหว่างutility 2>&1 > output.logและutility 2>&1 | tee output.logอยู่teeในนั้นซ้ำกับกระแสนั้นจะถูกต้องหรือไม่
แรงบันดาลใจ

ด้วยตัวอย่างของutility 2>&1 > output.log | more_stuffและutility >ouput.log| more_stuff , is the difference that more_stuff` มีเอาต์พุตข้อผิดพลาดมาตรฐานไปยังคอนโซลเป็นอินพุตmore_stuffหรือไม่ เนื่องจากในตัวอย่างที่สองไม่มีเอาต์พุตไปยังคอนโซลไม่มีอินพุตเป็นหลักmore_stuff? ถ้าใช่นั่นไม่ชัดเจนตั้งแต่ย่อหน้าก่อนหน้าคุณทราบว่าเอาต์พุตมาตรฐานไปที่ไฟล์และข้อผิดพลาดมาตรฐานไปที่คอนโซล
แรงบันดาลใจ

@Motivated ความคิดเห็นแรกของคุณดูเหมือนถูกต้องสำหรับฉันใช่ สำหรับความคิดเห็นที่สอง: ในคำสั่งแรกmore_stuffจะได้รับสิ่งที่utilityส่งไปยังกระแสข้อผิดพลาดเดิม (แต่ถูกเปลี่ยนเส้นทางไปยังเอาต์พุตมาตรฐาน) ไม่ได้เพราะมันจะจบลงบนคอนโซลถ้าmore_stuffไม่ได้มี แต่เป็นเพราะมันจะไปยังกระแสออกมาตรฐาน ในคำสั่งที่สองไม่more_stuffได้รับอะไรเนื่องจากไม่มีเอาต์พุตมาตรฐานจากด้านซ้ายมือของไพพ์ไลน์ กระแสข้อผิดพลาดจากutilityจะยังคงจบลงบนคอนโซลในคำสั่งที่ 2
Kusalananda

ขอบคุณ คุณหมายความว่าเพราะคำสั่งutility > output.log | more_stuffไม่ส่งผลให้เกิดการส่งออกในกระแสออกมาตรฐานจากมุมมองข้อผิดพลาดมาตรฐาน?
แรงบันดาลใจ

@Motivated เนื่องจากด้านซ้ายมือไม่ได้สร้างอะไรในเอาต์พุตมาตรฐาน (ถูกเปลี่ยนเส้นทาง) จะไม่มีการส่งข้อมูลผ่านไพพ์
Kusalananda

24

หมายเหตุบรรณาธิการ

กรุณาตรวจสอบให้แน่ใจว่าได้อ่านความคิดเห็นในคำตอบนี้ - derobert


คำตอบเดิม

2>&1 >output.logวิธีแรกเริ่มส่งทุกไฟล์จับ 2 สิ่ง (ข้อผิดพลาดมาตรฐาน) ไปยังแฟ้มจับที่ 1 (ออกมาตรฐาน) แล้วoutput.logส่งมาที่ไฟล์ กล่าวอีกนัยหนึ่งส่งข้อผิดพลาดมาตรฐานและเอาต์พุตมาตรฐานไปยังล็อกไฟล์

2>&1 | tee output.logเหมือนกันกับ2>&1บิตโดยรวมเอาท์พุทมาตรฐานและข้อผิดพลาดมาตรฐานไปยังสตรีมเอาต์พุตมาตรฐาน จากนั้นไปป์ที่ผ่านteeโปรแกรมซึ่งจะส่งอินพุตมาตรฐานไปยังเอาต์พุตมาตรฐาน (เช่นcat) และไปยังไฟล์ ดังนั้นจึงรวมสองสตรีม (ข้อผิดพลาดและเอาท์พุท) จากนั้นเอาท์พุทที่เทอร์มินัลและไฟล์

บรรทัดล่างคือว่าครั้งแรกที่ส่งstderr/ stdoutไปยังแฟ้มในขณะที่สองส่งไปทั้งไฟล์และการส่งออกมาตรฐาน (ซึ่งอาจจะเป็นขั้วถ้าคุณสร้างภายในอื่นซึ่งได้เปลี่ยนเส้นทางออกมาตรฐาน)

ฉันพูดถึงความเป็นไปได้ครั้งสุดท้ายเพราะคุณสามารถมีสิ่งที่ชอบ:

(echo hello | tee xyzzy.txt) >plugh.txt

เมื่อไม่มีสิ่งใดสิ้นสุดบนเครื่อง


13
-1 คุณมีไวยากรณ์ที่ถูกต้อง แต่ไม่ใช่ซีแมนทิกส์ เรียกใช้cat /doesnotexist 2>&1 >output.txt- คุณจะเห็นการcat: /doesnotexist: No such file or directoryแสดงให้เห็นถึงสถานีและ output.txt เป็นไฟล์ที่ว่างเปล่า ลำดับความสำคัญและการปิดกำลังเล่น: 2>&1(ซ้ำ fd2 จากfd1 ปัจจุบัน ) จากนั้น>output.txt(เปลี่ยนเส้นทาง fd1 ไปยัง output.txt ไม่เปลี่ยนสิ่งอื่นใด) เหตุผลที่2>&1 |แตกต่างกันเป็นเพราะลำดับความสำคัญ: ก่อน| >
Arcege

5
คำตอบนี้เป็นพื้นฐานที่ไม่ถูกต้องในหลักทุกประการ คำตอบหลายข้อด้านล่างดีกว่า แต่ฉันคิดว่าKusalananda คำนี้ชัดเจนที่สุด
Michael Homer

2
@ user14408: หากคุณเคยสร้างบัญชีในUnix & Linuxและอ้างสิทธิ์คำตอบนี้โปรดลบหมายเหตุบรรณาธิการของฉันทันทีที่คุณส่งความคิดเห็น
derobert

8

คำสั่งแรกจะทำงานอื่น:

หลังจาก

2>&1 > output.log 

STDOUT เก่าจะถูกบันทึก (คัดลอก) ใน STDERR จากนั้น STDOUT จะถูกเปลี่ยนเส้นทางไปยังไฟล์

ดังนั้น stdout จะไปที่ไฟล์และ stderr จะไปที่คอนโซล

และใน

 2>&1 | tee output.log

สตรีมทั้งสองจะถูกเปลี่ยนเส้นทางไปยังที Tee จะทำซ้ำอินพุตใด ๆ ไปยัง stdout (คอนโซลในกรณีของคุณ) และไปที่ไฟล์ ( output.log)

และมีรูปแบบอื่นก่อน:

    > output.log  2>&1

สิ่งนี้จะเปลี่ยนเส้นทางทั้ง STDOUT และ STDERR ไปยังไฟล์


4

อดีตส่งออกไปยังไฟล์เท่านั้น เอาต์พุตที่สองส่งไปยังไฟล์และไปยังหน้าจอ


4

เหตุผล2>&1 | teeคือเพื่อให้สามารถจับทั้ง stdout และ stderr ไปยังไฟล์บันทึกและดูบนหน้าจอในเวลาเดียวกัน สิ่งนี้สามารถทำได้เช่น>output.txt 2>&1 & tail -fกัน แต่คุณจะไม่รู้ว่าเมื่อใดที่คำสั่ง backgrounded ถูกยกเลิก - โปรแกรมถูกยกเลิกหรือมันทำงานโดยไม่มีเอาต์พุต 2>&1 | teeเป็นสำนวนที่พบบ่อยสำหรับโปรแกรมเมอร์


คุณหมายถึงพูดว่า 2> & 1> file.txt ตัวอย่างเช่นจะไม่จับทั้ง stdout และ stderr to file.txt?
แรงบันดาลใจ

0

ลองดูโค้ดตัวอย่างก่อน:

#include <stdio.h>
main() 
{
// message 1, on stdout (using  printf)
printf("%s",          "message 1, on stdout (using  printf)\n");

// message 2, on stdout (using fprintf)
fprintf(stdout, "%s", "message 2, on stdout (using fprintf)\n");

// message 3, on stderr (using fprintf)
fprintf(stderr, "%s", "message 3, on stderr (using fprintf)\n");
}

ให้เปรียบเทียบผลลัพธ์:
./helloerror
+ ไฟล์: ไม่มีข้อความ คอนโซล: ข้อความ 1,2,3;

./helloerror >error.txt
+ ไฟล์: ข้อความ 1,2; คอนโซล: ข้อความ 3;

./helloerror 2>&1 >error.txt
+ ไฟล์: ข้อความ 1,2; คอนโซล: ข้อความ 3;
+ เช่นเดียวกับ. /helloerror> error.txt

./helloerror >error.txt 2>&1
+ ไฟล์: ข้อความ 3,1,2; คอนโซล: ไม่มีข้อความ;
+ สังเกตคำสั่งที่ 3 เป็นลำดับแรกจากนั้น 1 จากนั้น 2

./helloerror | tee error.txt 2>&1
+ ไฟล์: ข้อความ 1,2; คอนโซล: ข้อความ 3,1,2;
+ สังเกตคำสั่งที่ 3 เป็นลำดับแรกจากนั้น 1 จากนั้น 2

./helloerror 2>&1 | tee error.txt
+ ไฟล์: ข้อความ 3,1,2; คอนโซล: ข้อความ 3,1,2;

วิธีใช้:
./helloerror >error.txt 2>&1
-> หากต้องการข้อความทั้งหมด (stdout + stderr) ในไฟล์ แต่ไม่ได้พิมพ์บนคอนโซล

./helloerror 2>&1 | tee error.txt
-> หากต้องการข้อความ (stdout + stderr) ทั้งหมดในไฟล์และพิมพ์บนคอนโซล


-1

นี่คือการสรุปโพสต์สตรีมเอาต์พุต Unix: http://www.devcodenote.com/2015/04/unix-output-streams.html

ตัวอย่างจากโพสต์:

มีเอาต์พุตสตรีมมาตรฐาน 3 รายการ:

STDIN - Standard Input - Writes from an input device to the program
STDOUT - Standard Output - Writes program output to screen unless specified otherwise.
STDERR - Standard Error Output - Writes error messages. Also printed to the screen unless specified otherwise.
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.