Buộc đệm dòng của stdout khi đường ống đến tee


117

Thông thường, stdoutđược đệm dòng. Nói cách khác, miễn là printfđối số của bạn kết thúc bằng một dòng mới, bạn có thể mong đợi dòng đó được in ngay lập tức. Điều này dường như không được giữ khi sử dụng đường ống để chuyển hướng đến tee.

Tôi có một chương trình C ++ a, xuất ra các chuỗi, luôn được kết thúc \n, thành stdout.

Khi nó được chạy bởi chính nó ( ./a), mọi thứ sẽ in chính xác và đúng thời điểm, như mong đợi. Tuy nhiên, nếu tôi đặt nó thành tee( ./a | tee output.txt), nó sẽ không in bất cứ thứ gì cho đến khi thoát ra, điều này làm mất đi mục đích sử dụng tee.

Tôi biết rằng tôi có thể sửa lỗi này bằng cách thêm dấu fflush(stdout)sau mỗi thao tác in trong chương trình C ++. Nhưng có cách nào sạch hơn, dễ dàng hơn không? Chẳng hạn, có lệnh nào tôi có thể chạy sẽ buộc stdoutphải được đệm dòng, ngay cả khi sử dụng đường ống không?

Câu trả lời:


67

Hãy thử unbuffercái nào là một phần của expectgói. Bạn có thể đã có nó trên hệ thống của mình.

Trong trường hợp của bạn, bạn sẽ sử dụng nó như thế này:

./a | unbuffer -p tee output.txt

( -pdành cho chế độ đường ống trong đó bộ đệm giải nén đọc từ stdin và chuyển nó tới lệnh trong phần còn lại của các đối số)


Cảm ơn, làm việc này, mặc dù tôi đã phải biên dịch expectbản thân mình như unbuffercó vẻ không được bao gồm theo mặc định trong OS X.
houbysoft

@houbysoft: Tôi rất vui vì nó hiệu quả với bạn. unbufferchỉ là một tập lệnh nhỏ nên bạn không cần phải biên dịch lại toàn bộ gói.
Tạm dừng cho đến khi có thông báo mới.

Ừ, có lẽ không, nhưng ./configure && makemất khoảng 10 giây và sau đó tôi vừa chuyển unbufferđến /usr/local/bin:)
houbysoft

3
Tôi đã cài đặt nó trên máy mac của mình (10.8.5) thông qua brew: brew install mong đợi --with-brewed-tk
Nils

2
FWIW, bởi vì bộ đệm hơi khó hiểu, cấu trúc có liên quan là như vậy unbuffer {commands with pipes/tee}.
Tên giả

128

bạn co thể thử stdbuf

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

(lớn) một phần của trang người đàn ông:

  -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.

hãy ghi nhớ điều này, mặc dù:

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.

bạn không chạy stdbuftrên tee, bạn đang chạy nó trên a, vì vậy điều này không nên ảnh hưởng đến bạn, trừ khi bạn thiết lập các vùng đệm của a'suối s trong a' s nguồn.

Ngoài ra, không phảistdbuf là POSIX, mà là một phần của GNU-coreutils.


3
Cảm ơn, nhưng điều này dường như không khả dụng trên OS X (câu hỏi được gắn thẻ osx-lion).
houbysoft

2
@houbysoft - Tôi khá chắc chắn công cụ GNU thể được cài đặt trên OS X
jordanm

1
@jordanm: có lẽ, nhưng cài đặt toàn bộ công cụ GNU loại có vẻ như quá mức cần thiết cho điều này ...
houbysoft

1
Đã ủng hộ câu trả lời này vì stdbufđã có sẵn trên các bản phân phối Centos Linux mà chúng tôi đang sử dụng nhưng unbufferkhông có. Cảm ơn!
Huw Walters

6
Đối với tập lệnh python, stdbuf sẽ không hoạt động, nhưng bạn có thể sử dụng -uđể vô hiệu hóa bộ đệm ở phía của python:python3 -u a.py | tee output.txt
Honza

27

Bạn cũng có thể thử thực hiện lệnh của mình trong một terminal giả bằng scriptlệnh (lệnh này sẽ thực thi đầu ra có đệm dòng vào đường ống)!

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

Lưu ý rằng scriptlệnh không truyền ngược lại trạng thái thoát của lệnh được bọc.


3
script -t 1 /path/to/outputfile.txt ./alàm việc tuyệt vời cho trường hợp sử dụng của tôi. Nó phát trực tiếp tất cả đầu ra outputfile.txttrong khi cũng in nó vào stdout của shell của bạn. Không cần phải sử dụngtee
Peter Berg

26

Bạn có thể sử dụng setlinebuf từ stdio.h.

setlinebuf(stdout);

Điều này sẽ thay đổi bộ đệm thành "dòng được đệm".

Nếu bạn cần linh hoạt hơn, bạn có thể sử dụng setvbuf.


8
Tôi tự hỏi tại sao giải pháp này có quá ít lượt ủng hộ. Đây là giải pháp duy nhất không tạo gánh nặng cho người gọi.
oxygene

1
Lưu ý rằng đây không phải là tiêu chuẩn C (hoặc thậm chí là POSIX). Nó có lẽ tốt hơn để sử dụng setvbuf(stdout, NULL, _IOLBF, 0), chính xác là tương đương.
rvighne

Điều này đã khắc phục sự cố của tôi trên OS X Catalina với một chương trình C ++ là printf () ing và tôi đang chuyển đến tee nhưng chỉ thấy đầu ra khi chương trình kết thúc.
jbaxter

2

Nếu bạn sử dụng các lớp dòng C ++ thay vào đó, mọi lớp đều std::endl một lần xả ngầm. Sử dụng kiểu in C, tôi nghĩ rằng phương pháp bạn đề xuất ( fflush()) là cách duy nhất.


4
Thật không may, điều này là không đúng sự thật. Bạn có thể quan sát hành vi tương tự với c ++ std :: cout ngay cả khi sử dụng std :: endl hoặc std :: flush. Bộ đệm xảy ra trên đầu trang và giải pháp dễ nhất trong Linux dường như là setlinebuf (stdout); như dòng đầu tiên trong hàm main () khi bạn là tác giả của chương trình và sử dụng các giải pháp khác ở trên khi không thể thay đổi mã nguồn.
oxygene

1
@oxygene Điều này không đúng. Tôi đã thử nó và endl làm sạch bộ đệm khi đường ống đến tee (không giống như với printf). Mã số: #include <iostream> #include <unistd.h> int main(void) { std::cout << "1" << std::endl; sleep(1); std::cout << "2" << std::endl; }. endl luôn làm sạch bộ đệm như được định nghĩa tại đây: en.cppreference.com/w/cpp/io/manip/endl
Curtis Yallop
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.