ในเปลือกหอย“ 2> & 1” หมายถึงอะไร


2282

ในเปลือก Unix ถ้าผมต้องการที่จะรวมstderrและstdoutเข้าสู่stdoutกระแสสำหรับการจัดการต่อไปผมสามารถผนวกต่อไปนี้ในตอนท้ายของคำสั่งของฉัน:

2>&1

ดังนั้นถ้าฉันต้องการใช้headกับผลลัพธ์จากg++ฉันสามารถทำสิ่งนี้:

g++ lots_of_errors 2>&1 | head

ดังนั้นฉันสามารถเห็นข้อผิดพลาดเพียงสองสามข้อแรกเท่านั้น

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

คนที่สามารถทำลายนี้และอธิบายทีละอักขระสิ่งที่2>&1 หมายถึง?


50
@dbr ฉันไม่คิดว่ามันเป็นเพียงแค่ทุบตี - ฉันเชื่อว่ามันเป็นสิ่งที่เปลือก bourne; เพราะฉะนั้น sh, bash, ksh, ash, dash, และอื่น ๆ
ปืน

8
นี่เป็นส่วนหนึ่งของย่อหน้าเปลี่ยนเส้นทางที่อธิบายถึงเชลล์ที่เข้ากับ POSIX หรือ POSIX เชลล์สำหรับสั้น ๆ ksh เป็นเปลือก POSIX เช่น ดู: pubs.opengroup.org/onlinepubs/009695399/utilities/…
jim mcnamara

12
โครงสร้างนี้ยังทำงานบน Windows
Vadzim

2
โดยทั่วไปแล้วจะทำได้ดี2>&1กว่า2> / dev / null ;-)
F. Hauri

11
ฉันคิดว่าฉันพูดถึงว่า|& เป็นการจดชวเลข2>&1 |ถ้าคุณใช้ zsh ฉันไม่สามารถพูดได้ว่ามันใช้กับเชลล์คล้ายบอร์นอื่น ๆ หรือว่าเป็นคุณสมบัติ zsh เท่านั้น
chrixian

คำตอบ:


2555

ตัวอธิบายไฟล์ 1 เป็นเอาต์พุตมาตรฐาน ( stdout)
ตัวอธิบายไฟล์ 2 เป็นข้อผิดพลาดมาตรฐาน ( stderr)

นี่เป็นวิธีหนึ่งที่จะจำนี้สร้าง (แม้ว่ามันจะไม่ถูกต้องทั้งหมด): ในตอนแรก2>1อาจมีลักษณะเหมือนเป็นวิธีที่ดีที่จะเปลี่ยนเส้นทางไปยังstderr stdoutอย่างไรก็ตามมันจะถูกตีความว่า "เปลี่ยนเส้นทางstderrไปยังไฟล์ชื่อ1" &บ่งชี้ว่าสิ่งต่อไปนี้คือไฟล์ descriptor ไม่ใช่ชื่อไฟล์ ดังนั้นโครงสร้างจึงกลายเป็น: 2>&1.


281
แต่มันไม่ควรจะเป็นอย่างนั้น&2>&1เหรอ?
dokaspar

319
@Dominik: ไม่&ถูกแปลความหมายเท่านั้น "file descriptor" ในบริบทของการเปลี่ยนเส้นทาง การเขียนcommand &2>&ถูกแยกวิเคราะห์เป็นcommand &และ2>&1คือ "ทำงานcommandในพื้นหลังจากนั้นเรียกใช้คำสั่ง2และเปลี่ยนเส้นทาง stdout ลงใน stdout"
Adam Rosenfield

15
ทำไมพวกเขาถึงเลือกสิ่งลึกลับเช่นนี้ แค่สงสัย.
CommaToast

81
แต่คุณจะเปลี่ยนเส้นทางไปยังไฟล์ชื่อ '& 1' ได้อย่างไร
Martín Fixman

120
@Martin:2>'&1'

632
echo test > afile.txt

afile.txtการเปลี่ยนเส้นทางไป stdout สิ่งนี้เหมือนกับการทำ

echo test 1> afile.txt

ในการเปลี่ยนเส้นทาง stderr คุณต้อง:

echo test 2> afile.txt

>& เป็นไวยากรณ์ในการเปลี่ยนเส้นทางสตรีมไปยังตัวอธิบายไฟล์อื่น - 0 คือ stdin, 1 คือ stdout และ 2 คือ stderr

คุณสามารถเปลี่ยนเส้นทาง stdout ไปยัง stderr โดยทำ:

echo test 1>&2 # or echo test >&2

หรือในทางกลับกัน:

echo test 2>&1

ดังนั้นโดยย่อ ... 2>เปลี่ยนเส้นทาง stderr ไปยังไฟล์ (ไม่ระบุ) การต่อท้าย&1เปลี่ยนเส้นทาง stderr ไปยัง stdout


5
สิ่งนี้มีเหตุผลสำหรับคุณjava ... 2&1 >> data.logไหมฉันเห็นเพื่อนร่วมงานคนหนึ่งทำสิ่งนี้
Thang Pham

5
@Harry ที่ดูเหมือนว่าทั้งเปลือกที่ไม่ทุบตีหรือพิมพ์ผิด .. cmd 2>&1 >> somefile.logจะผนวก stdout / stderr ไปยังไฟล์ - มันเป็นพื้นเดียวกันกับด้านบนด้วย>> fileเพื่อผนวก
dbr

73
@dbr cmd 2>&1 >>fileไม่เปลี่ยนเส้นทาง stderr ไปยังไฟล์ แต่cmd >> file 2>&1ทำเช่นนั้น เรื่องการสั่งซื้อ ในกรณีแรก stderr จะถูกเปลี่ยนเส้นทางไปยัง stdout ของเชลล์ (อาจเป็น tty หากคำสั่งถูกป้อนแบบโต้ตอบ) จากนั้น stdout จะถูกนำไปยังไฟล์ ในกรณีที่สอง stdout จะถูกนำไปยังไฟล์จากนั้น stderr จะถูกนำไปที่เดียวกัน
William Pursell

2
ฉันชอบคำตอบข้างต้น แต่อาจเป็นแบบสัมผัสที่ชัดเจนกว่า "2> & 1" เปลี่ยนเส้นทาง stderr ไปยังเป้าหมายของ stdout ดังนั้นหากคุณมีบางอย่างเช่น "ls -l >> directoryContents 2> & 1" ผลลัพธ์จะเป็นไฟล์ชื่อ directoryContents จะมีเนื้อหาของไดเรกทอรีทำงานต่อท้าย หากมีข้อผิดพลาดในการทำงาน: ข้อความแสดงข้อผิดพลาดจะถูกผนวกเข้ากับไฟล์ directoryContents ตามที่เกิดขึ้น
Max West

1
เป็น0(or 1,2)>&0(or 1,2)เหมือนตัวเลือกในการควบคุมเอาต์พุตหรือไม่? เป็นecho test >test.log 2>&1เหมือนกันecho test 2>&1 >test.logไหม
Simin Jie

318

เทคนิคบางอย่างเกี่ยวกับการเปลี่ยนเส้นทาง

ลักษณะเฉพาะบางอย่างของไวยากรณ์นี้อาจมีพฤติกรรมที่สำคัญ มีบางตัวอย่างเล็ก ๆ น้อย ๆ เกี่ยวกับการเปลี่ยนเส้นทางเป็นSTDERR, STDOUTและการขัดแย้งการสั่งซื้อ

1 - เขียนทับหรือต่อท้าย?

สัญลักษณ์>หมายถึงการเปลี่ยนเส้นทาง

  • >หมายถึงส่งไปยังไฟล์ที่เสร็จสมบูรณ์ทั้งหมดเขียนทับเป้าหมายหากมีอยู่ (ดูnoclobberคุณสมบัติทุบตีที่# 3 ในภายหลัง)
  • >>หมายถึงการส่งนอกจากนี้จะผนวกเข้ากับเป้าหมายหากมีอยู่

ไม่ว่าในกรณีใดไฟล์จะถูกสร้างขึ้นหากไม่มีอยู่

2 - บรรทัดคำสั่งเชลล์ขึ้นอยู่กับการสั่งซื้อ !!

สำหรับการทดสอบสิ่งนี้เราจำเป็นต้องมีคำสั่งง่ายๆซึ่งจะส่งบางสิ่งบางอย่างในเอาต์พุต :

$ ls -ld /tmp /tnt
ls: cannot access /tnt: No such file or directory
drwxrwxrwt 118 root root 196608 Jan  7 11:49 /tmp

$ ls -ld /tmp /tnt >/dev/null
ls: cannot access /tnt: No such file or directory

$ ls -ld /tmp /tnt 2>/dev/null
drwxrwxrwt 118 root root 196608 Jan  7 11:49 /tmp

(คาดว่าคุณไม่มีไดเรกทอรีชื่อ/tntแน่นอน;) เรามีมัน !!

ดังนั้นเรามาดู:

$ ls -ld /tmp /tnt >/dev/null
ls: cannot access /tnt: No such file or directory

$ ls -ld /tmp /tnt >/dev/null 2>&1

$ ls -ld /tmp /tnt 2>&1 >/dev/null
ls: cannot access /tnt: No such file or directory

บรรทัดคำสั่งสุดท้ายทิ้งSTDERRไปที่คอนโซลและดูเหมือนจะไม่เป็นพฤติกรรมที่คาดหวัง ... แต่ ...

หากคุณต้องการให้การโพสต์ตัวกรองบางอย่างเกี่ยวกับการส่งออกหนึ่งอื่น ๆ หรือทั้งสองอย่าง:

$ ls -ld /tmp /tnt | sed 's/^.*$/<-- & --->/'
ls: cannot access /tnt: No such file or directory
<-- drwxrwxrwt 118 root root 196608 Jan  7 12:02 /tmp --->

$ ls -ld /tmp /tnt 2>&1 | sed 's/^.*$/<-- & --->/'
<-- ls: cannot access /tnt: No such file or directory --->
<-- drwxrwxrwt 118 root root 196608 Jan  7 12:02 /tmp --->

$ ls -ld /tmp /tnt >/dev/null | sed 's/^.*$/<-- & --->/'
ls: cannot access /tnt: No such file or directory

$ ls -ld /tmp /tnt >/dev/null 2>&1 | sed 's/^.*$/<-- & --->/'

$ ls -ld /tmp /tnt 2>&1 >/dev/null | sed 's/^.*$/<-- & --->/'
<-- ls: cannot access /tnt: No such file or directory --->

โปรดสังเกตว่าบรรทัดคำสั่งสุดท้ายในย่อหน้านี้เหมือนกับในย่อหน้าก่อนหน้าซึ่งที่ฉันเขียนดูเหมือนจะไม่เป็นพฤติกรรมที่คาดหวัง (ดังนั้นนี่อาจเป็นพฤติกรรมที่คาดหวัง)

มีเทคนิคเล็กน้อยเกี่ยวกับการเปลี่ยนเส้นทางสำหรับ การดำเนินการที่แตกต่างกันในผลลัพธ์ทั้งสอง :

$ ( ls -ld /tmp /tnt | sed 's/^/O: /' >&9 ) 9>&2  2>&1  | sed 's/^/E: /'
O: drwxrwxrwt 118 root root 196608 Jan  7 12:13 /tmp
E: ls: cannot access /tnt: No such file or directory

หมายเหตุ: คำอธิบายถึงจะเกิดขึ้นเองตามธรรมชาติเพราะ&9) 9>&2

ภาคผนวก: nota! ด้วยเวอร์ชั่นใหม่ของ( >4.0) มีคุณสมบัติใหม่และไวยากรณ์ที่เซ็กซี่ยิ่งขึ้นสำหรับการทำสิ่งนี้:

$ ls -ld /tmp /tnt 2> >(sed 's/^/E: /') > >(sed 's/^/O: /')
O: drwxrwxrwt 17 root root 28672 Nov  5 23:00 /tmp
E: ls: cannot access /tnt: No such file or directory

และสุดท้ายสำหรับการจัดรูปแบบเอาต์พุตแบบเรียงซ้อนเช่น:

$ ((ls -ld /tmp /tnt |sed 's/^/O: /' >&9 ) 2>&1 |sed 's/^/E: /') 9>&1| cat -n
     1  O: drwxrwxrwt 118 root root 196608 Jan  7 12:29 /tmp
     2  E: ls: cannot access /tnt: No such file or directory

ภาคผนวก: nota! ไวยากรณ์ใหม่ที่เหมือนกันทั้งสองวิธี:

$ cat -n <(ls -ld /tmp /tnt 2> >(sed 's/^/E: /') > >(sed 's/^/O: /'))
     1  O: drwxrwxrwt 17 root root 28672 Nov  5 23:00 /tmp
     2  E: ls: cannot access /tnt: No such file or directory

ที่STDOUTผ่านตัวกรองที่เฉพาะเจาะจงSTDERRไปยังอีกและในที่สุดทั้งสองออกไปรวมกันผ่านตัวกรองคำสั่งที่สาม

3 - คำเกี่ยวกับnoclobberตัวเลือกและ>|ไวยากรณ์

เกี่ยวกับการเขียนทับ :

ในขณะที่set -o noclobberสั่งให้ bash ไม่เขียนทับไฟล์ใด ๆ ที่มีอยู่>|ไวยากรณ์จะให้คุณผ่านข้อ จำกัด นี้:

$ testfile=$(mktemp /tmp/testNoClobberDate-XXXXXX)

$ date > $testfile ; cat $testfile
Mon Jan  7 13:18:15 CET 2013

$ date > $testfile ; cat $testfile
Mon Jan  7 13:18:19 CET 2013

$ date > $testfile ; cat $testfile
Mon Jan  7 13:18:21 CET 2013

ไฟล์ถูกเขียนทับทุกครั้งตอนนี้:

$ set -o noclobber

$ date > $testfile ; cat $testfile
bash: /tmp/testNoClobberDate-WW1xi9: cannot overwrite existing file
Mon Jan  7 13:18:21 CET 2013

$ date > $testfile ; cat $testfile
bash: /tmp/testNoClobberDate-WW1xi9: cannot overwrite existing file
Mon Jan  7 13:18:21 CET 2013

ผ่านด้วย>|:

$ date >| $testfile ; cat $testfile
Mon Jan  7 13:18:58 CET 2013

$ date >| $testfile ; cat $testfile
Mon Jan  7 13:19:01 CET 2013

การยกเลิกการตั้งค่าตัวเลือกนี้และ / หรือสอบถามว่าได้ตั้งค่าไว้แล้ว

$ set -o | grep noclobber
noclobber           on

$ set +o noclobber

$ set -o | grep noclobber
noclobber           off

$ date > $testfile ; cat $testfile
Mon Jan  7 13:24:27 CET 2013

$ rm $testfile

4 - เคล็ดลับสุดท้ายและอื่น ๆ ...

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

$ ls -ld /tmp /tnt >/dev/null 2>&1

สำหรับกรณีพิเศษนี้จะมีไวยากรณ์ทางลัด: &>... หรือ>&

$ ls -ld /tmp /tnt &>/dev/null

$ ls -ld /tmp /tnt >&/dev/null

หมายเหตุ: หาก2>&1มีอยู่1>&2ก็เป็นไวยากรณ์ที่ถูกต้องเช่นกัน:

$ ls -ld /tmp /tnt 2>/dev/null 1>&2

4b- ตอนนี้ฉันจะให้คุณคิดถึง:

$ ls -ld /tmp /tnt 2>&1 1>&2  | sed -e s/^/++/
++/bin/ls: cannot access /tnt: No such file or directory
++drwxrwxrwt 193 root root 196608 Feb  9 11:08 /tmp/

$ ls -ld /tmp /tnt 1>&2 2>&1  | sed -e s/^/++/
/bin/ls: cannot access /tnt: No such file or directory
drwxrwxrwt 193 root root 196608 Feb  9 11:08 /tmp/

4c- หากคุณกำลังสนใจในการเพิ่มเติมข้อมูล

คุณสามารถอ่านคู่มือละเอียดได้โดยกดปุ่ม:

man -Len -Pless\ +/^REDIRECTION bash

ใน คอนโซล ;-)


5
อ่านเพิ่มเติม:ถ้าคุณชอบสิ่งนี้คุณอาจจะรู้สึกขอบคุณ: วิธีการละเมิดการเปลี่ยนเส้นทางสามารถให้พฤติกรรมที่แปลก
F. Hauri


130

ฉันพบโพสต์ที่ยอดเยี่ยมเกี่ยวกับการเปลี่ยนเส้นทาง: ทั้งหมดเกี่ยวกับการเปลี่ยนเส้นทาง

เปลี่ยนเส้นทางทั้งเอาต์พุตมาตรฐานและข้อผิดพลาดมาตรฐานไปยังไฟล์

ไฟล์ $ command &>

หนึ่งซับใช้ตัว&>ดำเนินการเพื่อเปลี่ยนทิศทางทั้งเอาต์พุตสตรีม - stdout และ stderr - จากคำสั่งไปยังไฟล์ นี่คือทางลัดของ Bash สำหรับการเปลี่ยนเส้นทางสตรีมทั้งสองไปยังปลายทางเดียวกันอย่างรวดเร็ว

นี่คือลักษณะของตาราง descriptor file หลังจาก Bash เปลี่ยนเส้นทางทั้งสองสตรีม:

ป้อนคำอธิบายภาพที่นี่

ที่คุณสามารถดูทั้ง stdout และ stderr fileตอนนี้ชี้ไปที่ ดังนั้นสิ่งที่เขียนไป stdout และ stderr fileได้รับการเขียนเพื่อ

มีหลายวิธีในการเปลี่ยนเส้นทางสตรีมทั้งสองไปยังปลายทางเดียวกัน คุณสามารถเปลี่ยนเส้นทางสตรีมได้ทีละรายการ:

$ command> ไฟล์ 2> & 1

นี่เป็นวิธีทั่วไปในการเปลี่ยนเส้นทางสตรีมไปยังไฟล์ stdout แรกถูกเปลี่ยนเส้นทางไปยังไฟล์จากนั้น stderr จะถูกทำซ้ำให้เหมือนกับ stdout fileดังนั้นทั้งลำธารจบลงด้วยการชี้ไปที่

เมื่อ Bash เห็นการเปลี่ยนเส้นทางหลายครั้งระบบจะประมวลผลจากซ้ายไปขวา ลองทำตามขั้นตอนและดูว่าเกิดอะไรขึ้น ก่อนเรียกใช้คำสั่งใด ๆ ตารางตัวอธิบายไฟล์ของ Bash จะมีลักษณะดังนี้:

ป้อนคำอธิบายภาพที่นี่

ตอนนี้ Bash ประมวลผลการเปลี่ยนเส้นทางไฟล์แรก เราเคยเห็นสิ่งนี้มาก่อนและทำให้ stdout ชี้ไปที่ไฟล์:

ป้อนคำอธิบายภาพที่นี่

Bash ถัดไปจะเห็นการเปลี่ยนเส้นทางที่สอง 2> & 1 เราไม่เคยเห็นการเปลี่ยนเส้นทางนี้มาก่อน อันนี้ซ้ำไฟล์ descriptor 2 เป็นสำเนาของ file descriptor 1 และเราได้:

ป้อนคำอธิบายภาพที่นี่

สตรีมทั้งสองถูกเปลี่ยนเส้นทางไปยังไฟล์แล้ว

อย่างไรก็ตามระวังที่นี่! การเขียน

คำสั่ง> ไฟล์ 2> & 1

ไม่เหมือนกับการเขียน:

$ command 2> & 1> ไฟล์

ลำดับการเปลี่ยนเส้นทางสำคัญใน Bash! คำสั่งนี้เปลี่ยนเส้นทางเฉพาะเอาต์พุตมาตรฐานไปยังไฟล์ stderr จะยังคงพิมพ์ไปยังสถานี เพื่อให้เข้าใจว่าทำไมสิ่งนั้นถึงเกิดขึ้น ดังนั้นก่อนรันคำสั่งตาราง descriptor ไฟล์จะเป็นดังนี้:

ป้อนคำอธิบายภาพที่นี่

ตอนนี้ Bash ประมวลผลการเปลี่ยนเส้นทางจากซ้ายไปขวา มันแรกเห็น 2> & 1 ดังนั้นจึงซ้ำ stderr เพื่อ stdout ตารางตัวอธิบายไฟล์กลายเป็น:

ป้อนคำอธิบายภาพที่นี่

ตอนนี้ Bash เห็นการเปลี่ยนเส้นทางครั้งที่สอง>fileและมันเปลี่ยนเส้นทาง stdout เป็นไฟล์:

ป้อนคำอธิบายภาพที่นี่

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

ยังทราบว่าใน Bash เขียน

ไฟล์ $ command &>

เหมือนกับ:

$ command> & ไฟล์


3
สองคนสุดท้ายจะแตกต่างกันถ้า "คำสั่ง" ลงท้ายด้วยตัวเลขตามนั้นจะถูกนำมาเป็นตัวบ่งชี้ไฟล์ตัวเลือกสำหรับ>&
MM

รูปวาดและคำอธิบายที่ดีมาก! คุณช่วยอธิบายว่า "ซ้ำ" หมายถึงอะไรจริงๆ คุณพูดถึง "อันนี้ [2> & 1] ไฟล์ descriptor ที่ซ้ำกัน 2 เป็นสำเนาของ file descriptor 1" ดูเหมือนว่า stderr จะซ้ำกับ stdout แต่ถ้าเป็นเช่นนั้นฉันควรจะเห็นว่าผิดพลาดด้วย/dev/tty0หรือไม่?
HCSF

87

ตัวเลขอ้างถึง file descriptors (fd)

  • ศูนย์คือ stdin
  • หนึ่งคือ stdout
  • สองคือ stderr

2>&1 เปลี่ยนเส้นทาง fd 2 เป็น 1

สิ่งนี้ใช้ได้กับไฟล์ descriptor จำนวนเท่าใดก็ได้หากโปรแกรมใช้

คุณสามารถดู/usr/include/unistd.hว่าคุณลืมพวกเขา:

/* Standard file descriptors.  */
#define STDIN_FILENO    0   /* Standard input.  */
#define STDOUT_FILENO   1   /* Standard output.  */
#define STDERR_FILENO   2   /* Standard error output.  */

ที่กล่าวว่าฉันได้เขียนเครื่องมือ C ที่ใช้อธิบายไฟล์ที่ไม่ได้มาตรฐานสำหรับการบันทึกที่กำหนดเองดังนั้นคุณจะไม่เห็นมันจนกว่าคุณจะเปลี่ยนเส้นทางไปยังไฟล์หรืออะไร


58

โครงสร้างนั้นส่งกระแสข้อผิดพลาดมาตรฐาน ( stderr) ไปยังตำแหน่งปัจจุบันของเอาต์พุตมาตรฐาน ( stdout) - ปัญหาสกุลเงินนี้ดูเหมือนจะถูกละเลยโดยคำตอบอื่น ๆ

คุณสามารถเปลี่ยนทิศทางตัวจัดการเอาต์พุตใด ๆ ไปยังวิธีอื่นได้โดยใช้วิธีนี้ แต่ส่วนใหญ่มักจะใช้เพื่อส่งสัญญาณstdoutและstderrสตรีมไปยังสตรีมเดียวสำหรับการประมวลผล

ตัวอย่างบางส่วนคือ:

# Look for ERROR string in both stdout and stderr.
foo 2>&1 | grep ERROR

# Run the less pager without stderr screwing up the output.
foo 2>&1 | less

# Send stdout/err to file (with append) and terminal.
foo 2>&1 |tee /dev/tty >>outfile

# Send stderr to normal location and stdout to file.
foo >outfile1 2>&1 >outfile2

หมายเหตุว่าสุดท้ายจะไม่ตรงstderrไปoutfile2- มันเปลี่ยนเส้นทางไปยังสิ่งที่stdoutเป็นข้อโต้แย้งเมื่อถูกพบ ( outfile1) และแล้วเปลี่ยนเส้นทางไปยังstdoutoutfile2

วิธีนี้ช่วยให้ใช้กลอุบายที่ซับซ้อนได้


5
แม้ว่าตัวอย่างสุดท้ายนั้นจะมีความชัดเจนมากขึ้นเช่น: foo> outfile2 2> outfile1
Michael Cramer

3
ชัดเจนขึ้นใช่ แต่นั่นจะไม่แสดงลักษณะการเปลี่ยนเส้นทาง "ตำแหน่ง" ตัวอย่างมีการวางแผนไว้เนื่องจากโดยปกติแล้วจะไม่เป็นประโยชน์ในการทำสิ่งนี้ในบรรทัดเดียว - วิธีการนี้มีประโยชน์จริง ๆ เมื่อฝ่ายต่าง ๆ มีหน้าที่รับผิดชอบในส่วนต่าง ๆ ของการเปลี่ยนเส้นทาง ตัวอย่างเช่นเมื่อสคริปต์ทำการเปลี่ยนเส้นทางหนึ่งบิตและคุณเรียกใช้ด้วยบิตอื่น
paxdiablo

5
ฉันเพิ่งตระหนักว่าตัวอย่างที่ผ่านมานอกจากนี้ยังช่วยแก้ปัญหาความสับสนมานานผมได้เกี่ยวกับว่าทำไมนี้: ไม่ทำงานเช่นนี้some_program 2>&1 > /dev/null some_program > /dev/null 2>&1
snapfractalpop

ความคิดเห็นของคุณเกี่ยวกับตัวอย่างสุดท้ายมีค่าเป็นตัวอักษรสีทอง :-) ฉันไม่เคยคิดว่าข้อโต้แย้งการเปลี่ยนเส้นทางเหล่านี้เป็นตำแหน่ง ... ฉันคิดว่านี่เป็นสิ่งสำคัญที่ควรรู้
Nils-o-mat

20

2>&1เป็นโครงสร้าง POSIX เชลล์ นี่คือรายละเอียดโทเค็นโดยโทเค็น:


2: ตัวระบุไฟล์เอาต์พุต " ข้อผิดพลาดมาตรฐาน "

>&: ทำซ้ำโอเปอเรเตอร์ตัวอธิบายไฟล์เอาต์พุต (ตัวแปรของโอเปอเรเตอร์การเปลี่ยนเส้นทางเอาต์พุต> ) Given [x]>&[y], อธิบายไฟล์แสดงโดยทำจะเป็นสำเนาของอธิบายไฟล์เอาท์พุทxy

1ตัวระบุไฟล์เอาต์พุต " เอาต์พุตมาตรฐาน "

นิพจน์2>&1คัดลอกไฟล์ descriptor 1ไปยังตำแหน่ง2ดังนั้นเอาต์พุตใด ๆ ที่เขียนไปยัง2("ข้อผิดพลาดมาตรฐาน") ในสภาพแวดล้อมการดำเนินการจะไปที่ไฟล์เดียวกันซึ่งเดิมอธิบายโดย1("เอาต์พุตมาตรฐาน")


คำอธิบายเพิ่มเติม:

ตัวอธิบายไฟล์ : "เลขจำนวนเต็มเฉพาะต่อกระบวนการที่ไม่ซ้ำกันซึ่งใช้เพื่อระบุไฟล์ที่เปิดอยู่เพื่อวัตถุประสงค์ในการเข้าถึงไฟล์"

เอาต์พุต / ข้อผิดพลาดมาตรฐาน : อ้างถึงหมายเหตุต่อไปนี้ในส่วนRedirectionของเอกสารเชลล์:

ไฟล์ที่เปิดจะแสดงด้วยเลขทศนิยมเริ่มต้นด้วยศูนย์ ค่าที่เป็นไปได้ที่ใหญ่ที่สุดคือการกำหนดการใช้งาน อย่างไรก็ตามการใช้งานทั้งหมดจะต้องสนับสนุนอย่างน้อย 0 ถึง 9 โดยรวมเพื่อการใช้งานโดยแอปพลิเคชัน หมายเลขเหล่านี้เรียกว่า "file descriptors" ค่า 0, 1 และ 2 มีความหมายพิเศษและการใช้งานทั่วไปและมีนัยโดยการดำเนินการเปลี่ยนเส้นทางบางอย่าง พวกเขาจะเรียกว่าอินพุตมาตรฐานเอาต์พุตมาตรฐานและข้อผิดพลาดมาตรฐานตามลำดับ โปรแกรมมักจะรับอินพุตจากอินพุตมาตรฐานและเขียนเอาต์พุตลงในเอาต์พุตมาตรฐาน ข้อความผิดพลาดมักจะเขียนข้อผิดพลาดมาตรฐาน โอเปอเรเตอร์การเปลี่ยนเส้นทางสามารถนำหน้าด้วยหนึ่งหรือมากกว่าหนึ่งหลัก (โดยไม่อนุญาตให้มีการแทรกอักขระ) เพื่อกำหนดหมายเลขตัวอธิบายไฟล์


19

2 เป็นข้อผิดพลาดมาตรฐานของคอนโซล

1 คือเอาต์พุตมาตรฐานคอนโซล

นี่คือ Unix มาตรฐานและ Windows ก็ใช้ POSIX เช่นกัน

เช่นเมื่อคุณวิ่ง

perl test.pl 2>&1

ข้อผิดพลาดมาตรฐานถูกเปลี่ยนเส้นทางไปยังเอาต์พุตมาตรฐานดังนั้นคุณสามารถเห็นทั้งสองเอาต์พุตพร้อมกัน:

perl test.pl > debug.log 2>&1

หลังจากดำเนินการแล้วคุณจะเห็นผลลัพธ์ทั้งหมดรวมถึงข้อผิดพลาดใน debug.log

perl test.pl 1>out.log 2>err.log

จากนั้นเอาต์พุตมาตรฐานจะไปที่ out.log และข้อผิดพลาดมาตรฐานไปที่ err.log

ฉันแนะนำให้คุณลองทำความเข้าใจกับสิ่งเหล่านี้


ตัวอย่างที่สองเป็นความผิด: เป็นคำสั่งมีความสำคัญSTDERRถูกเปลี่ยนเส้นทางไปSTDOUTเพียงเริ่มต้นSTDOUTจะถูกเขียนไปdebug.log (ไม่STDERR ) ดูคำตอบของฉัน (วรรค # 2)! เพื่อให้แน่ใจว่าทั้งสองจะถูกเปลี่ยนเส้นทางไปยังไฟล์เดียวกันคุณต้องสลับคำสั่งการเปลี่ยนเส้นทาง:perl test.pl > debug.log 2>&1
F. Hauri

16

เพื่อตอบคำถามของคุณ: ใช้ข้อผิดพลาดเอาต์พุต (ปกติส่งไปยัง stderr) และเขียนไปยังเอาต์พุตมาตรฐาน (stdout)

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

เพื่อช่วยให้คุณจำได้

  • 1 = เอาต์พุตมาตรฐาน (โดยที่โปรแกรมพิมพ์เอาต์พุตปกติ)
  • 2 = ข้อผิดพลาดมาตรฐาน (โดยที่โปรแกรมพิมพ์ข้อผิดพลาด)

"2> & 1" เพียงชี้ทุกอย่างที่ส่งไปยัง stderr ไปที่ stdout แทน

ฉันขอแนะนำให้อ่านโพสต์นี้เมื่อเกิดข้อผิดพลาดในการเปลี่ยนเส้นทางโดยครอบคลุมหัวข้อนี้อย่างละเอียด


11

จากมุมมองของโปรแกรมเมอร์มันหมายถึงสิ่งนี้อย่างแม่นยำ:

dup2(1, 2);

ดูหน้าคน

ความเข้าใจที่2>&1เป็นสำเนายังอธิบายว่าทำไม ...

command >file 2>&1

... ไม่เหมือนกับ ...

command 2>&1 >file

เป็นครั้งแรกที่ทั้งสองจะส่งกระแสไปfileในขณะที่สองจะส่งข้อผิดพลาดและเอาท์พุทธรรมดาเป็นstdoutfile


9

ฉันพบว่ามีประโยชน์มากถ้าคุณเป็นมือใหม่อ่านนี่

อัปเดต:
ในระบบ Linux หรือ Unix มีสองที่ที่โปรแกรมส่งเอาต์พุตไปที่: เอาต์พุตมาตรฐาน (stdout) และ Standard Error (stderr)คุณสามารถเปลี่ยนทิศทางเอาต์พุตเหล่านี้ไปยังไฟล์ใดก็ได้

เช่นถ้าคุณทำสิ่งนี้

ls -a > output.txt

จะไม่มีการพิมพ์ในคอนโซลเอาต์พุตทั้งหมด(stdout)จะถูกเปลี่ยนเส้นทางไปยังไฟล์เอาต์พุต

และถ้าคุณลองพิมพ์เนื้อหาของไฟล์ใด ๆ ที่ไม่ออกหมายความว่าเอาต์พุตจะเป็นข้อผิดพลาดเช่นถ้าคุณพิมพ์ test.txt ที่ไม่ปรากฏในไดเรกทอรีปัจจุบัน

cat test.txt > error.txt

ผลลัพธ์จะเป็น

cat: test.txt :No such file or directory

แต่ไฟล์ error.txt จะว่างเปล่าเพราะเราเปลี่ยนเส้นทาง stdout ไปยังไฟล์ไม่ใช่ stderr

ดังนั้นเราต้องการ file descriptor (file descriptor ไม่มีอะไรมากไปกว่าจำนวนเต็มบวกที่แทนไฟล์ open คุณสามารถบอกว่า descriptor เป็น id เฉพาะของไฟล์) เพื่อบอกเชลล์ว่าเอาต์พุตชนิดใดที่เราส่งไปยังไฟล์ในระบบ Unix / Linux 1 สำหรับ stdout และ 2 stderr

ดังนั้นถ้าคุณทำเช่นนี้

ls -a 1> output.txtหมายความว่าคุณกำลังส่งเอาต์พุตมาตรฐาน (stdout) ไปยัง output.txt

และถ้าคุณทำเช่นนี้

cat test.txt 2> error.txtหมายความว่าคุณกำลังส่งข้อผิดพลาดมาตรฐาน (stderr) ไปที่ error.txt

&1ถูกใช้เพื่ออ้างอิงค่าของ file descriptor 1 (stdout)

ตอนนี้ถึงจุดหมายความ2>&1ว่า "เปลี่ยน stderr ไปยังสถานที่เดียวกันเรากำลังเปลี่ยนเส้นทาง stdout"

ตอนนี้คุณสามารถทำได้

cat maybefile.txt > output.txt 2>&1

ทั้ง Standard output (stdout) และ Standard Error (stderr) จะถูกเปลี่ยนเส้นทางไปยัง output.txt

ขอบคุณOndrej K. ที่ชี้ให้เห็น


1
คำตอบสำหรับลิงก์เท่านั้นเป็นปัญหา ลิงก์อาจสูญเสียการแสดงผลคำตอบที่ไร้ประโยชน์ คุณควรมีรายละเอียดเพียงพอในคำตอบเสมอ
Ondrej K.

7

คนจำไว้เสมอpaxdiablo 's คำแนะนำเกี่ยวกับปัจจุบันสถานที่ตั้งของเป้าหมายการเปลี่ยนเส้นทาง ... มันเป็นสิ่งที่สำคัญ

ตัวช่วยจำส่วนตัวของฉันสำหรับ2>&1โอเปอเรเตอร์คือ:

  • คิดว่า&เป็นความหมาย'and'หรือ'add'(ตัวละครเป็นแอมป์ - และใช่มั้ย)
  • ดังนั้นมันจะกลายเป็น: 'เปลี่ยนเส้นทาง2(stderr) ไปที่1(stdout) แล้ว / อยู่ในขณะนี้และเพิ่มทั้งลำธาร'

ตัวช่วยจำเดียวกันทำงานได้สำหรับการเปลี่ยนเส้นทางที่ใช้บ่อยอื่น ๆ ด้วย1>&2:

  • นึกถึง&ความหมายandหรือadd... (คุณได้ความคิดเกี่ยวกับเครื่องหมายและใช่ไหม)
  • ดังนั้นมันจะกลายเป็น: 'เปลี่ยนเส้นทาง1(stdout) ไปที่2(stderr) แล้ว / อยู่ในขณะนี้และเพิ่มทั้งลำธาร'

และจำไว้เสมอว่าคุณต้องอ่านกลุ่มของการเปลี่ยนเส้นทางจากต้นจนจบจากขวาไปซ้าย ( ไม่ใช่จากซ้ายไปขวา)


7

การเปลี่ยนเส้นทางอินพุต

การเปลี่ยนทิศทางอินพุตทำให้ไฟล์ที่มีชื่อเป็นผลลัพธ์จากการขยายคำที่จะเปิดสำหรับการอ่านไฟล์ descriptor n หรืออินพุตมาตรฐาน (file descriptor 0) หากไม่ได้ระบุ n

รูปแบบทั่วไปสำหรับการเปลี่ยนเส้นทางอินพุตคือ:

[n]<word

เปลี่ยนเส้นทางเอาท์พุท

การเปลี่ยนทิศทางของเอาต์พุตทำให้ไฟล์ที่มีชื่อเป็นผลลัพธ์จากการขยายคำที่จะเปิดสำหรับการเขียนบน file descriptor n หรือเอาต์พุตมาตรฐาน (file descriptor 1) หากไม่ได้ระบุ n หากไฟล์ไม่มีอยู่ไฟล์จะถูกสร้างขึ้น ถ้ามันมีอยู่มันจะถูกปัดเศษเป็นขนาดศูนย์

รูปแบบทั่วไปสำหรับการเปลี่ยนเส้นทางผลลัพธ์คือ:

[n]>word

การย้ายไฟล์ Descriptors

ผู้ประกอบการเปลี่ยนเส้นทาง

[n]<&digit-

ย้ายไฟล์ descriptor digit ไปที่ file descriptor n หรืออินพุตมาตรฐาน (file descriptor 0) หากไม่ได้ระบุ n ตัวเลขถูกปิดหลังจากถูกทำซ้ำกับ n

ในทำนองเดียวกันผู้ประกอบการเปลี่ยนเส้นทาง

[n]>&digit-

ย้ายไฟล์ descriptor ดิจิตไปเป็น file descriptor n หรือเอาต์พุตมาตรฐาน (file descriptor 1) หากไม่ได้ระบุ n

Ref:

man bash

พิมพ์/^REDIRECTเพื่อค้นหาไปยังredirectionส่วนและเรียนรู้เพิ่มเติม ...

เวอร์ชันออนไลน์อยู่ที่นี่: 3.6 การเปลี่ยนเส้นทาง

PS:

หลายครั้งที่manเป็นเครื่องมือที่มีประสิทธิภาพในการเรียนรู้ Linux


6

ระบุว่า/fooไม่มีอยู่ในระบบของคุณและ/tmpไม่มี ...

$ ls -l /tmp /foo

จะพิมพ์เนื้อหาของ/tmpและพิมพ์ข้อความแสดงข้อผิดพลาด/foo

$ ls -l /tmp /foo > /dev/null

จะส่งเนื้อหาของ/tmpไปยัง/dev/nullและพิมพ์ข้อความแสดงข้อผิดพลาดสำหรับ/foo

$ ls -l /tmp /foo 1> /dev/null

จะทำสิ่งเดียวกัน (หมายเหตุ1 )

$ ls -l /tmp /foo 2> /dev/null

จะพิมพ์เนื้อหาของ/tmpและส่งข้อความแสดงข้อผิดพลาดไปที่/dev/null

$ ls -l /tmp /foo 1> /dev/null 2> /dev/null

จะส่งทั้งรายชื่อและข้อความผิดพลาดไปที่ /dev/null

$ ls -l /tmp /foo > /dev/null 2> &1

ชวเลข


5

นี่เหมือนกับการส่งข้อผิดพลาดไปยัง stdout หรือเทอร์มินัล

นั่นคือcmdไม่ใช่คำสั่ง:

$cmd 2>filename
cat filename

command not found

ข้อผิดพลาดถูกส่งไปยังไฟล์เช่นนี้:

2>&1

ข้อผิดพลาดมาตรฐานถูกส่งไปยังเทอร์มินัล


1

0 สำหรับอินพุต 1 สำหรับ stdout และ 2 สำหรับ stderr

One Tip : somecmd >1.txt 2>&1ถูกต้องในขณะที่ผิดsomecmd 2>&1 >1.txtทั้งหมดโดยไม่มีผลกระทบ!


1

unix_commands 2>&1

ใช้เพื่อพิมพ์ข้อผิดพลาดไปยังเทอร์มินัล

ต่อไปนี้แสดงให้เห็นถึงกระบวนการ

  • เมื่อมีการผลิตข้อผิดพลาดจะถูกเขียนลงใน&2บัฟเฟอร์หน่วยความจำข้อผิดพลาดมาตรฐานซึ่ง2อ้างอิงถึงสตรีมข้อผิดพลาดมาตรฐาน
  • เมื่อสร้างเอาต์พุตเอาต์พุตจะถูกเขียนลงในแอดเดรส&1บัฟเฟอร์หน่วยความจำเอาต์พุตมาตรฐานซึ่ง1อ้างอิงสตรีมเอาต์พุตมาตรฐาน

ดังนั้นให้unix_commandsสตรีมข้อผิดพลาดมาตรฐาน2และเปลี่ยนเส้นทาง>สตรีม (ของข้อผิดพลาด) ไปยังที่อยู่หน่วยความจำเอาท์พุทมาตรฐาน&1เพื่อให้พวกเขาจะถูกสตรีมไปยังเทอร์มินัลและพิมพ์

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