Đường ống, làm thế nào để lưu lượng dữ liệu trong một đường ống?


22

Tôi không hiểu làm thế nào dữ liệu chảy trong đường ống và hy vọng ai đó có thể làm rõ những gì đang xảy ra ở đó.

Tôi nghĩ rằng một đường dẫn của các lệnh xử lý các tệp (văn bản, mảng của chuỗi) theo cách thức theo dòng. (Nếu mỗi lệnh tự hoạt động theo từng dòng.) Mỗi ​​dòng văn bản đi qua đường ống, các lệnh không đợi trước đó để xử lý toàn bộ đầu vào.

Nhưng có vẻ như không phải vậy.

Dưới đây là một ví dụ thử nghiệm. Có một số dòng văn bản. Tôi viết hoa chúng và lặp lại mỗi dòng hai lần. Tôi làm như vậy với cat text | tr '[:lower:]' '[:upper:]' | sed 'p'.

Để làm theo quy trình, chúng ta có thể chạy nó "tương tác" - bỏ qua tên tệp đầu vào cat. Mỗi phần của đường ống chạy từng dòng:

$ cat | tr '[:lower:]' '[:upper:]'
alkjsd
ALKJSD
sdkj
SDKJ
$ cat | sed 'p'
line1
line1
line1
line 2
line 2
line 2

Nhưng đường ống hoàn chỉnh sẽ đợi tôi hoàn thành đầu vào EOFvà chỉ sau đó in kết quả:

$ cat | tr '[:lower:]' '[:upper:]' | sed 'p'
I am writing...
keep writing...
now ctrl-D
I AM WRITING...
I AM WRITING...
KEEP WRITING...
KEEP WRITING...
NOW CTRL-D
NOW CTRL-D

Có phải là như vậy? Tại sao nó không phải là từng dòng một?


Đó không phải là đường ống, nó là catbộ đệm cho đến khi stdin đóng lại.
goldilocks

nhưng trsedthực hiện các dòng quy trình từ cattrước khi stdin đóng cửa
xealits

Mặc định được sử dụng bởi stdio (mà tôi tin rằng tất cả các chương trình được đề cập sử dụng) là stderr không có bộ đệm và thiết bị xuất chuẩn được đệm khi ghi vào thiết bị đầu cuối và được đệm hoàn toàn theo cách khác (ví dụ nếu nó được ghi vào tệp hoặc đường ống) . Một số lệnh có cờ có thể thay đổi bộ đệm tiêu chuẩn, nhưng có vẻ như tr không có.
kasperd

Câu trả lời:


36

Có một quy tắc đệm chung theo sau là thư viện I / O tiêu chuẩn C ( stdio) mà hầu hết các chương trình unix sử dụng. Nếu đầu ra đi đến một thiết bị đầu cuối, nó được tuôn ra ở cuối mỗi dòng; mặt khác, nó chỉ bị xóa khi bộ đệm (8K trên hệ thống Linux / amd64 của tôi; có thể khác với bộ đệm của bạn).

Nếu tất cả các tiện ích của bạn đi theo quy tắc chung, bạn sẽ thấy đầu ra chậm trễ trong tất cả các ví dụ của bạn ( cat|sed, cat|trcat|tr|sed). Nhưng có một ngoại lệ: GNU catkhông bao giờ đệm đầu ra của nó. Nó không sử dụng stdiohoặc nó thay đổi stdiochính sách đệm mặc định .

Tôi có thể khá chắc chắn rằng bạn đang sử dụng GNU catchứ không phải một số unix khác catvì những người khác sẽ không hành xử theo cách này. Unix truyền thống catcó một -utùy chọn để yêu cầu đầu ra không có bộ đệm. GNU catbỏ qua -utùy chọn này vì đầu ra của nó luôn không có bộ đệm.

Vì vậy, bất cứ khi nào bạn có một đường ống catở bên trái, trong hệ thống GNU, việc truyền dữ liệu qua đường ống sẽ không bị trì hoãn. Các catthậm chí không được đi từng dòng - thiết bị đầu cuối của bạn được làm điều đó. Trong khi bạn đang nhập dữ liệu cho mèo, thiết bị đầu cuối của bạn ở chế độ "chính tắc" - dựa trên dòng, với các phím chỉnh sửa như backspace và ctrl-U cung cấp cho bạn cơ hội chỉnh sửa dòng bạn đã nhập trước khi gửi Enter.

Trong cat|tr|sedví dụ, trvẫn nhận được dữ liệu catngay sau khi bạn nhấn Enter, nhưng trtuân theo stdiochính sách mặc định: đầu ra của nó sẽ chuyển sang một đường ống, do đó, nó không tuôn ra sau mỗi dòng. Nó ghi vào ống thứ hai khi bộ đệm đầy hoặc khi nhận được EOF, tùy theo cái nào đến trước.

sedcũng tuân theo stdiochính sách mặc định, nhưng đầu ra của nó sẽ đến một thiết bị đầu cuối nên nó sẽ ghi từng dòng ngay sau khi hoàn thành. Điều này có ảnh hưởng đến số lượng bạn phải nhập trước khi có thứ gì đó xuất hiện ở đầu kia của đường ống - nếu sedbị chặn bộ đệm đầu ra của nó, bạn phải nhập gấp đôi (để điền vào trbộ đệm đầu ra sed đầu ra của đệm).

GNU sed-utùy chọn vì vậy nếu bạn đảo ngược thứ tự và sử dụng, cat|sed -u|trbạn sẽ thấy đầu ra xuất hiện lại ngay lập tức. ( sed -uTùy chọn có thể có sẵn ở nơi khác nhưng tôi không nghĩ đó là truyền thống unix cổ đại như cat -u) Theo như tôi có thể nói không có tùy chọn tương đương cho tr.

Có một tiện ích được gọi là stdbufcho phép bạn thay đổi chế độ đệm của bất kỳ lệnh nào sử dụng stdiomặc định. Nó hơi mong manh vì nó sử dụng LD_PRELOADđể hoàn thành một cái gì đó mà thư viện C không được thiết kế để hỗ trợ, nhưng trong trường hợp này nó dường như hoạt động:

cat | stdbuf -o 0 tr '[:lower:]' '[:upper:]' | sed 'p'

1
cảm ơn! Câu trả lời tuyệt vời. Có lẽ tôi nên đề cập đến bộ đệm trong câu hỏi theo cách nào đó, để người ta có thể tìm thấy nó.
xealits

teeddcũng thường chơi theo luật riêng của họ. Khi được kết hợp một cách tưởng tượng, ba công cụ có thể phủ nhận một cách rõ ràng bất kỳ nhu cầu nào đối với stdbufcác đường ống nền.
mikeerv

1
Đây là một trong những lý do để tránh việc sử dụng mèo vô dụng .
hobbs 2/2/2015

8

Điều này thực sự khiến tôi phải suy nghĩ để hiểu và thậm chí nhiều hơn để trả lời. Câu hỏi tuyệt vời (tôi sẽ đưa ra câu hỏi tiếp theo).

Bạn đã bỏ qua để thử tr | sedtrong các mục gỡ lỗi của bạn ở trên:

>tr '[:lower:]' '[:upper:]' | sed 'p'
i am writing
still writing
now ctrl-d
I AM WRITING
I AM WRITING
STILL WRITING
STILL WRITING
NOW CTRL-D
NOW CTRL-D
>

Vì vậy, rõ ràng là trbộ đệm. Học điều mới mỗi ngày!

CHỈNH SỬA :

Khi tôi nghĩ vậy, chúng tôi đã cô lập nguyên nhân, nhưng không đưa ra lời giải thích. Nếu bạn cat | tr, nó viết ngay lập tức, nếu bạn cat | sed, nó viết ngay lập tức, nhưng nếu bạn tr | sed, nó chờ đợi cho EOF. Tôi sẽ đề nghị câu trả lời có thể được chôn trong trhoặc sedmã nguồn sau đó, và không phải là một vấn đề đường ống.

CHỈNH SỬA :

Tôi thấy Wumpus cung cấp lời giải thích trong khi tôi đang gõ bản chỉnh sửa cuối cùng. Cảm ơn!


1
quả thực họ đệm! và thử nghiệm với khoảng 8kb dòng, như Wumpus đã đề cập, cho thấy bộ đệm thực sự là 8Kb. Tôi muốn chấp nhận cả hai câu trả lời để chia sẻ danh tiếng xung quanh, nhưng tôi sẽ coi Wumpus là một câu trả lời đầy đủ hơn. Dù sao cũng cảm ơn bạn!
xealits

1
Không có vấn đề gì, tôi là câu trả lời theo kinh nghiệm, anh ấy là người hiểu biết.
Poisson Aerohead

Xem thêm câu hỏi này cho thấy cách sử dụng stdbufcũng có thể hữu ích. unix.stackexchange.com/questions/182537/NH
Joe
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.