บังคับให้ไลน์บัฟเฟอร์ของ stdout เมื่อส่งไปที่ที


118

โดยปกติจะstdoutเป็นบรรทัดบัฟเฟอร์ กล่าวอีกนัยหนึ่งคือตราบใดที่printfอาร์กิวเมนต์ของคุณจบลงด้วยการขึ้นบรรทัดใหม่คุณสามารถคาดหวังว่าจะพิมพ์บรรทัดได้ทันที teeนี้จะไม่ปรากฏการระงับเมื่อใช้ท่อเปลี่ยนเส้นทางไปยัง

ฉันมีโปรแกรม C ++ aที่ส่งออกสตริงเสมอ - เทอร์มิ\nนนิ่งเป็นstdout.

เมื่อรันด้วยตัวเอง ( ./a) ทุกอย่างจะพิมพ์ออกมาอย่างถูกต้องและในเวลาที่เหมาะสมตามที่คาด แต่ถ้าฉันท่อมันtee( ./a | tee output.txt) teeก็ไม่ได้พิมพ์อะไรจนกว่าจะจบการทำงานซึ่งขัดแย้งกับวัตถุประสงค์ของการใช้

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

คำตอบ:


67

ลองunbufferซึ่งเป็นส่วนหนึ่งของexpectแพ็คเกจ คุณอาจมีอยู่แล้วในระบบของคุณ

ในกรณีของคุณคุณจะใช้สิ่งนี้:

./a | unbuffer -p tee output.txt

( -pสำหรับโหมดไปป์ไลน์ที่ unbuffer อ่านจาก stdin และส่งต่อไปยังคำสั่งในส่วนที่เหลือของอาร์กิวเมนต์)


ขอบคุณสิ่งนี้ใช้งานได้แม้ว่าฉันจะต้องรวบรวมexpectตัวเองunbufferแต่ดูเหมือนจะไม่รวมอยู่ใน OS X โดยค่าเริ่มต้น
houbysoft

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

ใช่อาจจะไม่ แต่./configure && makeใช้เวลาประมาณ 10 วินาทีจากนั้นฉันก็ย้ายunbufferไปที่/usr/local/bin:)
houbysoft

3
ฉันได้รับการติดตั้งบนเครื่อง Mac ของฉัน (10.8.5) ผ่านการชง: การติดตั้งแบบชงคาดว่าจะมี - ด้วยการชง - tk
Nils

2
FWIW เพราะ unbuffer unbuffer {commands with pipes/tee}ค่อนข้างสับสนโครงสร้างที่เกี่ยวข้องคือ
ชื่อปลอม

128

คุณสามารถลอง stdbuf

$ stdbuf -o 0 ./a | tee output.txt

(ใหญ่) ส่วนหนึ่งของหน้าคน:

  -i, --input=MODE   adjust standard input stream buffering
  -o, --output=MODE  adjust standard output stream buffering
  -e, --error=MODE   adjust standard error stream buffering

If MODE is 'L' the corresponding stream will be line buffered.
This option is invalid with standard input.

If MODE is '0' the corresponding stream will be unbuffered.

Otherwise MODE is a number which may be followed by one of the following:
KB 1000, K 1024, MB 1000*1000, M 1024*1024, and so on for G, T, P, E, Z, Y.
In this case the corresponding stream will be fully buffered with the buffer
size set to MODE bytes.

โปรดจำไว้ว่า:

NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does
for e.g.) then that will override corresponding settings changed by 'stdbuf'.
Also some filters (like 'dd' and 'cat' etc.) dont use streams for I/O,
and are thus unaffected by 'stdbuf' settings.

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

นอกจากนี้ยังstdbufเป็นไม่ได้ POSIX แต่เป็นส่วนหนึ่งของการใช้ GNU coreutils


3
ขอบคุณ แต่ดูเหมือนจะไม่มีใน OS X (คำถามคือแท็ก osx-lion)
houbysoft

2
@houbysoft - ฉันค่อนข้างมั่นใจว่าเครื่องมือ GNU สามารถติดตั้งบน OS X ได้
jordanm

1
@jordanm: บางที แต่การติดตั้งเครื่องมือ GNU ทั้งหมดดูเหมือนจะเกินความจำเป็นสำหรับสิ่งนี้ ...
houbysoft

1
เพิ่มคะแนนคำตอบนี้เนื่องจากstdbufมีอยู่แล้วในการกระจาย Centos Linux ที่เราใช้อยู่และunbufferไม่ใช่ ขอบคุณ!
Huw Walters

6
สำหรับสคริปต์ python stdbuf จะไม่ทำงาน แต่คุณสามารถใช้-uปิดการใช้งานบัฟเฟอร์ที่ด้านข้างของ python ได้:python3 -u a.py | tee output.txt
Honza

27

คุณอาจลองดำเนินการคำสั่งของคุณในเทอร์มินัลหลอกโดยใช้scriptคำสั่ง (ซึ่งควรบังคับใช้เอาต์พุตบรรทัดบัฟเฟอร์ไปยังไพพ์)!

script -q /dev/null ./a | tee output.txt     # Mac OS X, FreeBSD
script -c "./a" /dev/null | tee output.txt   # Linux

โปรดทราบว่าscriptคำสั่งไม่เผยแพร่สถานะการออกของคำสั่งที่รวมไว้


3
script -t 1 /path/to/outputfile.txt ./aใช้งานได้ดีสำหรับกรณีการใช้งานของฉัน ถ่ายทอดสดเอาต์พุตทั้งหมดไปoutputfile.txtยังขณะที่พิมพ์ไปยัง stdout ของเชลล์ของคุณ ไม่จำเป็นต้องใช้tee
Peter Berg

26

คุณสามารถใช้ setlinebuf จาก stdio.h

setlinebuf(stdout);

สิ่งนี้ควรเปลี่ยนการบัฟเฟอร์เป็น "line buffered"

หากคุณต้องการความยืดหยุ่นมากขึ้นคุณสามารถใช้ setvbuf


8
ฉันสงสัยว่าทำไมโซลูชันนี้จึงมีการโหวตเพิ่มขึ้นน้อยมาก นี่เป็นทางออกเดียวที่ไม่สร้างภาระให้กับผู้โทร
oxygene

1
โปรดทราบว่านี่ไม่ใช่มาตรฐาน C (หรือแม้แต่ POSIX) น่าจะดีกว่าที่จะใช้setvbuf(stdout, NULL, _IOLBF, 0)ซึ่งเทียบเท่ากันทุกประการ
rvighne

สิ่งนี้แก้ไขปัญหาของฉันใน OS X Catalina ด้วยโปรแกรม C ++ ซึ่งเป็น printf () ing และฉันกำลังไปที่ที แต่เห็นเฉพาะผลลัพธ์เมื่อโปรแกรมเสร็จสิ้น
jbaxter

2

ถ้าคุณใช้การเรียน c ++ กระแสแทนทุกstd::endl เป็นล้างโดยปริยาย การใช้การพิมพ์รูปแบบ C ฉันคิดว่าวิธีที่คุณแนะนำ ( fflush()) เป็นวิธีเดียว


4
น่าเสียดายที่นี่ไม่เป็นความจริง คุณสามารถสังเกตพฤติกรรมเดียวกันได้ด้วย c ++ std :: cout แม้ว่าจะใช้ std :: endl หรือ std :: flush การบัฟเฟอร์เกิดขึ้นที่ด้านบนและวิธีที่ง่ายที่สุดใน Linux ดูเหมือนว่า setlinebuf (stdout); เป็นบรรทัดแรกใน main () เมื่อคุณเป็นผู้เขียนโปรแกรมและใช้วิธีแก้ไขปัญหาอื่น ๆ ข้างต้นเมื่อไม่สามารถเปลี่ยนซอร์สโค้ดได้
oxygene

1
@oxygene นี่ไม่เป็นความจริง ฉันลองแล้ว endl จะล้างบัฟเฟอร์เมื่อไปที่ที (ไม่เหมือนกับ printf) รหัส: #include <iostream> #include <unistd.h> int main(void) { std::cout << "1" << std::endl; sleep(1); std::cout << "2" << std::endl; }. endl จะล้าง
Curtis Yallop

0

unbufferคำสั่งจากexpectแพคเกจที่@Paused จนกว่าจะมีประกาศคำตอบไม่ได้ทำงานให้ฉันวิธีที่มันถูกนำเสนอ

แทนที่จะใช้:

./a | unbuffer -p tee output.txt

ฉันต้องใช้:

unbuffer -p ./a | tee output.txt

( -pสำหรับโหมดไปป์ไลน์ที่ unbuffer อ่านจาก stdin และส่งต่อไปยังคำสั่งในส่วนที่เหลือของอาร์กิวเมนต์)

expectแพคเกจสามารถติดตั้งบน:

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