Làm cách nào để sử dụng tee để chuyển hướng đến grep


13

Tôi không có nhiều kinh nghiệm sử dụng tee, vì vậy tôi hy vọng điều này không cơ bản lắm.

Sau khi xem một trong những câu trả lời cho câu hỏi này, tôi đã bắt gặp một hành vi kỳ lạ với tee.

Để tôi xuất dòng đầu tiên và dòng tìm thấy, tôi có thể sử dụng dòng này:

ps aux | tee >(head -n1) | grep syslog
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4

Tuy nhiên, lần đầu tiên tôi chạy cái này (tính bằng zsh), kết quả là không đúng thứ tự, các tiêu đề cột nằm dưới kết quả grep (tuy nhiên điều này đã không xảy ra lần nữa), vì vậy tôi đã cố gắng trao đổi các lệnh xung quanh:

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

Chỉ có dòng đầu tiên được in, và không có gì khác! Tôi có thể sử dụng tee để chuyển hướng đến grep không, hoặc tôi đang làm điều này sai cách?

Khi tôi gõ câu hỏi này, lệnh thứ hai thực sự hoạt động một lần đối với tôi, tôi đã chạy lại năm lần và sau đó quay lại kết quả một dòng. Đây chỉ là hệ thống của tôi? (Tôi đang chạy zsh trong tmux).

Cuối cùng, tại sao với lệnh đầu tiên là "grep syslog" không được hiển thị dưới dạng kết quả (chỉ có một kết quả)?

Để kiểm soát ở đây là grep mà không có tee

ps aux | grep syslog
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4
henry    2290  0.0  0.1  95220  3092 ?        Ssl  Sep07   3:12 /usr/bin/pulseaudio --start --log-target=syslog
henry   15924  0.0  0.0   3128   824 pts/4    S+   13:44   0:00 grep syslog

Cập nhật: Có vẻ như phần đầu đang khiến toàn bộ lệnh bị cắt bớt (như được chỉ ra trong câu trả lời bên dưới), lệnh bên dưới hiện đang trả về như sau:

ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
syslog     806

Không phải là một câu trả lời trực tiếp cho câu hỏi của bạn nhưng sẽ tốt hơn nhiều nếu chỉ làm một cái gì đó như thế ps aux | sed -n -e '1p' -e '/syslog/p'.
jw013

Tôi thậm chí không bao giờ nghĩ về sed, tôi nghĩ rằng đó có thể là một câu trả lời phù hợp cho câu hỏi liên quan ở đây nhưng tôi thực sự đang tìm kiếm thông tin về hành vi không nhất quán của các lệnh này!
Rqomey

Câu trả lời:


19
$ ps aux | tee >(head -n1) | grep syslog
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND 
syslog     806  0.0  0.0  34600   824 ?        Sl   Sep07   0:00 rsyslogd -c4

Các lệnh grepheadbắt đầu cùng một lúc và cả hai đều nhận được cùng một dữ liệu đầu vào, nhưng nói chung, khi dữ liệu có sẵn. Có một số điều có thể giới thiệu đầu ra 'không đồng bộ', lật các dòng; ví dụ:

  1. Dữ liệu được ghép từ teethực tế được gửi đến một quá trình trước khi khác, phụ thuộc chủ yếu vào việc thực hiện tee. Một teetriển khai đơn giản sẽ có readmột số lượng đầu vào, và sau writeđó hai lần: Một lần để xuất chuẩn và một lần cho đối số của nó. Điều này có nghĩa là một trong những điểm đến đó sẽ nhận được dữ liệu đầu tiên.

    Tuy nhiên, đường ống đều được đệm. Có khả năng các bộ đệm này là 1 dòng mỗi dòng, nhưng chúng có thể lớn hơn, điều này có thể khiến một trong các lệnh nhận thấy mọi thứ nó cần cho đầu ra (ví dụ: grepdòng ped) trước khi lệnh khác ( head) nhận được bất kỳ dữ liệu nào tại tất cả.

  2. Mặc dù ở trên, cũng có thể một trong những lệnh này nhận được dữ liệu nhưng không thể làm gì với nó kịp thời, và sau đó lệnh kia nhận được nhiều dữ liệu hơn và xử lý nhanh chóng.

    Ví dụ, ngay cả khi headgrepđược gửi dữ liệu từng dòng một, nếu headkhông biết cách xử lý (hoặc bị trì hoãn bởi lập lịch kernel), grepcó thể hiển thị kết quả trước khi headcó cơ hội. Để chứng minh, hãy thử thêm một độ trễ: ps aux | tee >(sleep 1; head -n1) | grep syslogĐiều này gần như chắc chắn sẽ xuất grepđầu ra đầu tiên.

$ ps aux | tee >(grep syslog) | head -n1
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND

Tôi tin rằng bạn thường chỉ nhận được một dòng ở đây, bởi vì headnhận được dòng đầu tiên đầu tiên và sau đó đóng stdin và thoát. Khi teethấy rằng thiết bị xuất chuẩn của nó đã bị đóng, sau đó nó đóng stdin của chính nó (đầu ra từ ps) và thoát. Điều này có thể phụ thuộc vào việc thực hiện.

Thực tế, dữ liệu duy nhất psđược gửi là dòng đầu tiên (chắc chắn, vì headđang kiểm soát điều này) và có thể một số dòng khác trước head& teeđóng mô tả stdin của chúng.

Sự không nhất quán với việc liệu dòng thứ hai xuất hiện có được giới thiệu theo thời gian hay không: headđóng stdin, nhưng psvẫn đang gửi dữ liệu. Hai sự kiện này không được đồng bộ hóa tốt, do đó, dòng chứa syslogvẫn có cơ hội biến nó teethành đối số ( greplệnh). Điều này tương tự như các giải thích ở trên.

Bạn có thể tránh vấn đề này hoàn toàn bằng cách sử dụng các lệnh chờ tất cả đầu vào trước khi đóng stdin / thoát. Ví dụ: sử dụng awkthay vì head, nó sẽ đọc và xử lý tất cả các dòng của nó (ngay cả khi chúng không gây ra đầu ra):

ps aux | tee >(grep syslog) | awk 'NR == 1'

Nhưng lưu ý rằng các dòng vẫn có thể xuất hiện không theo thứ tự, như trên, có thể được chứng minh bằng:

ps aux | tee >(grep syslog) | (sleep 1; awk 'NR == 1')

Hy vọng điều này không quá nhiều chi tiết, nhưng có rất nhiều thứ đồng thời tương tác với nhau. Các quy trình riêng biệt chạy đồng thời mà không có bất kỳ đồng bộ hóa nào, vì vậy hành động của chúng đối với bất kỳ hoạt động cụ thể nào có thể khác nhau; đôi khi nó giúp đào sâu vào các quy trình cơ bản để giải thích tại sao.


1
Câu trả lời tuyệt vời! Tôi thực sự hỏi bởi vì tôi quan tâm đến các quy trình cơ bản. Khi mọi thứ bất tiện tôi thấy nó thú vị. Sẽ có một cách tốt hơn để chạy ps aux | tee >(grep syslog) | head -n1mà sẽ dừng headđóng thiết bị xuất chuẩn. Ồ, lệnh này đã bắt đầu cung cấp đầu ra ngay bây giờ, nhưng như sẽ xảy ra theo câu trả lời của bạn, nó dường như bị cắt ngắnUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND syslog 806
Rqomey

1
Bạn có thể sử dụng một cái gì đó không đóng stdin thay vì head. Tôi đã cập nhật câu trả lời với ví dụ này:ps aux | tee >(grep syslog) | awk 'NR == 1'
mrb

1
@KrzysztofAdamski, khi bạn sử dụng >(cmd), shell sẽ tạo một đường ống có tên và chuyển nó thành đối số cho lệnh ( tee). Sau đó, teeviết vào stdout (piped to awk) và cũng cho đối số đó. Nó giống như mkfifo a_fifo ; grep ... a_fifotrong một vỏ và ps | tee a_fifo | awk ...trong một vỏ khác.
mrb

1
@KrzysztofAdamski gnu.org/software/bash/manual/html_node/... - Thử echo >(exit 0), mà sẽ echo đối số thực tế thông qua vỏ (trong trường hợp của tôi, nó trở nên /dev/fd/63). Điều này sẽ làm việc tương tự trên bash và zsh.
mrb

1
@mrb: đó là một tính năng rất thú vị mà tôi chưa từng biết trước đây, cảm ơn bạn. Nó đang hoạt động theo một cách kỳ lạ trong bash, tuy nhiên, hãy xem pastebin.com/xFgRcJdF . Thật không may, tôi không có thời gian để điều tra việc này bây giờ nhưng tôi sẽ làm nó vào ngày mai.
Krzysztof Adamski

2

grep syslogkhông phải lúc nào cũng được hiển thị vì nó phụ thuộc vào thời gian. Khi sử dụng shell pipe, bạn đang chạy các lệnh gần như đồng thời. Nhưng điều quan trọng ở đây là từ "gần như". Nếu pskết thúc quá trình quét tất cả các quy trình trước khi grep được khởi chạy, nó sẽ không có trong danh sách. Bạn có thể nhận được kết quả ngẫu nhiên tùy thuộc vào tải của hệ thống, v.v.

Điều tương tự xảy ra với tee của bạn. Nó được chạy trên nền trong subshell và nó có thể được bắn trước hoặc sau grep. Đây là lý do tại sao thứ tự đầu ra không nhất quán.

Đối với câu hỏi tee, hành vi của nó khá lạ. Điều này là do nó không được sử dụng theo cách thông thường. Nó được chạy mà không có bất kỳ đối số nào có nghĩa là nó chỉ nên sao chép dữ liệu từ stdin sang stdout. Nhưng thiết bị xuất chuẩn của nó được chuyển hướng đến đầu chạy con (trong trường hợp đầu tiên) hoặc grep (trường hợp thứ 2). Nhưng nó cũng được dẫn đến lệnh tiếp theo. Tôi nghĩ rằng những gì xảy ra trong trường hợp này thực sự phụ thuộc vào việc thực hiện. Ví dụ, trên bash 4.2.28 của tôi, không có gì được viết cho subshell stdin. Trên zsh, nó hoạt động đáng tin cậy theo cách bạn muốn (in cả dòng đầu tiên của dòng ps và dòng tìm kiếm), mỗi lần tôi thử,


Điều đó giải thích một điều dù sao, tôi ngạc nhiên rằng tee trì hoãn grep chạy đến một mức độ đáng chú ý!
Rqomey

0

Một chút hackish, nhưng đây là giải pháp của tôi, dưới dạng psgrep()hàm shell tôi sử dụng:

Chuyển hướng pshàng tiêu đề sang STDERR, sau đó grepbật STDOUT, nhưng trước tiên hãy xóa grepchính lệnh đó để tránh hàng "nhiễu" xuất phát từ grepchính nó:

psgrep() { ps aux | tee >(head -1>&2) | grep -v " grep $@" | grep "$@" -i --color=auto; }
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.