วิธีเปลี่ยนเส้นทางเอาต์พุตไปยังไฟล์และ stdout


988

ใน bash การโทรfooจะแสดงเอาต์พุตใด ๆ จากคำสั่งนั้นบน stdout

การโทรfoo > outputจะเปลี่ยนเส้นทางเอาต์พุตใด ๆ จากคำสั่งนั้นไปยังไฟล์ที่ระบุ (ในกรณีนี้คือ 'เอาต์พุต')

มีวิธีการเปลี่ยนเส้นทางการส่งออกไปยังไฟล์และมันจะแสดงบน stdout หรือไม่?


3
หากใครบางคนลงเอยด้วยการมองหาการจับเอาท์พุทข้อผิดพลาดในไฟล์ลองดูที่ - unix.stackexchange.com/questions/132511//
Murphy

2
ทราบเกี่ยวกับคำศัพท์: เมื่อคุณดำเนินการfoo > outputข้อมูลจะถูกเขียนไป stdout และ stdout เป็นoutputไฟล์ที่ชื่อว่า นั่นคือการเขียนไฟล์เป็นการเขียนไปยัง stdout คุณกำลังถามว่าเป็นไปได้ที่จะเขียนทั้งสองไปยัง stdout และไปยังสถานี
William Pursell

2
@WilliamPursell ฉันไม่แน่ใจว่าการอธิบายของคุณจะปรับปรุงสิ่งต่าง ๆ :-) แล้วเรื่องนี้: OP ถามว่ามันเป็นไปได้ไหมที่จะส่งstdout ของโปรแกรมที่เรียกไปยังทั้งไฟล์และstdout ของโปรแกรมที่เรียก (หลังเป็น stdout ที่โปรแกรมที่เรียก รับช่วงหากไม่มีสิ่งใดพิเศษที่ทำได้เช่นเทอร์มินัลหากโปรแกรมการโทรเป็นเซสชัน bash แบบโต้ตอบ) และบางทีพวกเขาก็ต้องการที่จะกำกับstderrของโปรแกรมที่เรียกว่าในทำนองเดียวกัน ("เอาต์พุตใด ๆ จากคำสั่งนั้น" อาจตีความได้ว่ามีความหมายรวมถึง stderr)
Don Hatch

คำตอบ:


1379

คำสั่งที่คุณต้องการมีชื่อว่าtee:

foo | tee output.file

ตัวอย่างเช่นหากคุณสนใจเฉพาะ stdout:

ls -a | tee output.file

หากคุณต้องการรวม stderr ให้ทำ:

program [arguments...] 2>&1 | tee outfile

2>&1เปลี่ยนเส้นทางช่อง 2 (stderr / ข้อผิดพลาดมาตรฐาน) ไปที่ช่อง 1 (stdout / เอาต์พุตมาตรฐาน) ซึ่งทั้งสองถูกเขียนเป็น stdout นอกจากนี้ยังนำไปยังไฟล์เอาต์พุตที่กำหนดตามteeคำสั่ง

นอกจากนี้หากคุณต้องการต่อท้ายไฟล์บันทึกให้ใช้tee -aเป็น:

program [arguments...] 2>&1 | tee -a outfile

173
ถ้า OP ต้องการให้ "all output" ถูกเปลี่ยนเส้นทางคุณจะต้องคว้า stderr: "ls -lR / 2> & 1 | tee output.file"
James Brady

45
@evanmcdonnal คำตอบไม่ผิดมันอาจจะไม่เจาะจงพอหรือสมบูรณ์ขึ้นอยู่กับความต้องการของคุณ มีเงื่อนไขที่คุณอาจไม่ต้องการ stderr เนื่องจากเป็นส่วนหนึ่งของเอาต์พุตที่บันทึกไว้ในไฟล์ เมื่อฉันตอบ 5 ปีที่ผ่านมาฉันสันนิษฐานว่า OP ต้องการ stdout เท่านั้นเนื่องจากเขาพูดถึง stdout ในหัวข้อของการโพสต์
Zoredache

3
อาขอโทษฉันอาจจะสับสนเล็กน้อย เมื่อฉันลองมันฉันไม่ได้ผลลัพธ์บางทีมันอาจจะเป็น stderr ทั้งหมด
evanmcdonnal

38
ใช้-aอาร์กิวเมนต์teeเพื่อผนวกเนื้อหาoutput.fileแทนการเขียนทับ:ls -lR / | tee -a output.file
kazy

16
หากคุณใช้งานใน$?ภายหลังระบบจะส่งคืนรหัสสถานะteeซึ่งอาจไม่ใช่สิ่งที่คุณต้องการ ${PIPESTATUS[0]}แต่คุณสามารถใช้
LaGoutte

501
$ program [arguments...] 2>&1 | tee outfile

2>&1ดัมพ์ stderr และ stdout stream tee outfileใช้สตรีมที่ได้รับและเขียนไปยังหน้าจอและไปที่ไฟล์ "outfile"

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

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

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

อัปเดต: โปรแกรมunbufferซึ่งเป็นส่วนหนึ่งของexpectแพ็คเกจจะแก้ปัญหาบัฟเฟอร์ ซึ่งจะทำให้เกิด stdout และ stderr teeการเขียนไปยังหน้าจอและไฟล์ได้ทันทีและเก็บไว้ในซิงค์เมื่อมีการรวมกันและเปลี่ยนเส้นทางไปยัง เช่น:

$ unbuffer program [arguments...] 2>&1 | tee outfile

7
ใครรู้ว่า 'unbuffer' สำหรับ osx?
John Mee

7
หากคุณไม่สามารถใช้ unbuffer ได้ GNU coreutils จะมีเครื่องมือที่เรียกว่าstdbuffซึ่งสามารถใช้ป้องกันการบัฟเฟอร์เอาต์พุตเช่นนั้นได้$ stdbuf -o 0 program [arguments...] 2>&1 | tee outfile
Peter

1
หากคุณใช้ OS X เป็นกรณีส่วนใหญ่ที่จะใช้ตัวถอดบัฟเฟอร์ในกรณีส่วนใหญ่แม้ว่าจะไม่ใช่หากคุณต้องการส่งสัญญาณไปยังคำสั่งที่คุณใช้อยู่ ความคิดเห็นของฉันมุ่งเน้นไปที่ผู้ใช้ที่อยู่บน Linux ซึ่ง coreutils มักถูกติดตั้งเป็นค่าเริ่มต้น @Ethan โปรดดูคำตอบนี้หากคุณสับสนเกี่ยวกับไวยากรณ์ของ stdbuf: stackoverflow.com/a/25548995/942781
Peter

3
โปรดทราบว่าpythonมีตัวเลือกในตัว-uที่ unbuffers เอาท์พุท
fabian789

1
ผู้ช่วยชีวิตที่สมบูรณ์สำหรับการฝึกอบรม ML รุ่นที่ฉันต้องการเข้าสู่ระบบโดยไม่ต้องใช้รหัสเพิ่มเติม แต่ยังเห็นผลลัพธ์ในชั่วโมง / วันที่ใช้ในการทำงานขอขอบคุณ!
โรโคโค

126

อีกวิธีหนึ่งที่เหมาะกับฉันคือ

<command> |& tee  <outputFile>

ดังที่แสดงในคู่มือ gnu bash

ตัวอย่าง:

ls |& tee files.txt

หากใช้ '| &' ข้อผิดพลาดมาตรฐานของ command1 นอกเหนือจากเอาต์พุตมาตรฐานจะเชื่อมต่อกับอินพุตมาตรฐานของ command2 ผ่านไพพ์ มันเป็นชวเลขสำหรับ 2> & 1 | การเปลี่ยนทิศทางโดยนัยของข้อผิดพลาดมาตรฐานไปยังเอาต์พุตมาตรฐานถูกดำเนินการหลังจากการเปลี่ยนทิศทางใด ๆ ที่ระบุโดยคำสั่ง

สำหรับข้อมูลเพิ่มเติมอ้างอิงการเปลี่ยนเส้นทาง


10
หากคุณใช้งานใน$?ภายหลังระบบจะส่งคืนรหัสสถานะteeซึ่งอาจไม่ใช่สิ่งที่คุณต้องการ ${PIPESTATUS[0]}แต่คุณสามารถใช้
LaGoutte

1
วิธีนี้จะทิ้งเอาท์พุทคอนโซลสีแนวคิดใด ๆ
shakram02

ลอง: unbuffer ls -l --color = auto | tee files.txt
Luciano Andress Martini


11

สิ่งที่จะเพิ่ม ...

แพ็คเกจ unbuffer มีปัญหาการสนับสนุนกับแพคเกจบางอย่างภายใต้ fedora และ redhat unix รีลีส

หลีกเลี่ยงปัญหา

ทำงานต่อไปนี้สำหรับฉัน

bash myscript.sh 2>&1 | tee output.log

ขอบคุณScDFและmatthewอินพุตของคุณช่วยฉันได้มาก ...



3

คำตอบโบนัสตั้งแต่กรณีการใช้งานนำฉันมาที่นี่:

ในกรณีที่คุณต้องทำเช่นนี้กับผู้ใช้อื่น

echo "some output" | sudo -u some_user tee /some/path/some_file

โปรดทราบว่าเสียงสะท้อนจะเกิดขึ้นเมื่อคุณและการเขียนไฟล์จะเกิดขึ้นเป็น "some_user" สิ่งที่จะไม่ทำงานคือถ้าคุณเรียกใช้เสียงสะท้อนเป็น "some_user" และเปลี่ยนเส้นทางผลลัพธ์ด้วย >> "some_file" เนื่องจากการเปลี่ยนเส้นทางไฟล์จะเกิดขึ้น อย่างคุณ.

คำแนะนำ: ทียังสนับสนุนการผนวกด้วยแฟล็ก -a หากคุณต้องการแทนที่บรรทัดในไฟล์เป็นผู้ใช้อื่นคุณสามารถเรียกใช้ sed เป็นผู้ใช้ที่ต้องการ


2

< command > |& tee filename # นี้จะสร้างไฟล์ "ชื่อไฟล์" ที่มีสถานะคำสั่งเป็นเนื้อหาหากไฟล์มีอยู่แล้วมันจะลบเนื้อหาที่มีอยู่และเขียนสถานะคำสั่ง

< command > | tee >> filename # นี้จะผนวกสถานะกับไฟล์ แต่จะไม่พิมพ์สถานะคำสั่งใน standard_output (หน้าจอ)

ฉันต้องการพิมพ์บางอย่างโดยใช้ "echo" บนหน้าจอและต่อท้ายข้อมูลที่สะท้อนไปยังไฟล์

echo "hi there, Have to print this on screen and append to a file" 

1

คุณสามารถทำสิ่งนี้กับสคริปต์ทั้งหมดของคุณโดยใช้บางอย่างเช่นที่จุดเริ่มต้นของสคริปต์ของคุณ:

#!/usr/bin/env bash

test x$1 = x$'\x00' && shift || { set -o pipefail ; ( exec 2>&1 ; $0 $'\x00' "$@" ) | tee mylogfile ; exit $? ; }

# do whaetever you want

การเปลี่ยนเส้นทางนี้ทั้ง stderr stdout และเอาท์พุทไปยังไฟล์ที่เรียกว่าmylogfile และปล่อยให้ทุกอย่างเป็นไปที่ stdout ในเวลาเดียวกัน

มันใช้กลเม็ดโง่ ๆ

  • ใช้execโดยไม่มีคำสั่งเพื่อตั้งค่าการเปลี่ยนเส้นทาง
  • ใช้teeเพื่อทำซ้ำเอาต์พุต
  • รีสตาร์ทสคริปต์ด้วยการเปลี่ยนเส้นทางที่ต้องการ
  • ใช้พารามิเตอร์พิเศษตัวแรก ( NULตัวอักษรอย่างง่ายที่ระบุโดย$'string'สัญกรณ์ทุบตีพิเศษ) เพื่อระบุว่าสคริปต์จะเริ่มต้นใหม่ (ไม่สามารถใช้พารามิเตอร์ที่เทียบเท่าโดยงานต้นฉบับของคุณ)
  • พยายามรักษาสถานะการออกดั้งเดิมเมื่อรีสตาร์ทสคริปต์โดยใช้pipefailตัวเลือก

น่าเกลียด แต่มีประโยชน์สำหรับฉันในบางสถานการณ์


-13

ทีสมบูรณ์แบบสำหรับสิ่งนี้ แต่มันจะทำงานเช่นกัน

ls -lr / > output | cat output

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

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