Làm thế nào tôi có thể gửi thiết bị xuất chuẩn đến nhiều lệnh?


186

Có một số lệnh lọc hoặc hành động trên đầu vào, và sau đó chuyển nó thành đầu ra, tôi nghĩ là thường stdout- nhưng một số lệnh sẽ chỉ thực hiện stdinvà làm bất cứ điều gì chúng làm với nó, và không xuất ra gì.

Tôi quen thuộc nhất với OS X và vì vậy có hai ý tưởng xuất hiện ngay lập tức pbcopypbpaste- đó là phương tiện truy cập vào bảng tạm hệ thống.

Nhưng dù sao, tôi biết rằng nếu tôi muốn lấy thiết bị xuất chuẩn và nhổ đầu ra để đi đến cả hai stdoutvà một tệp thì tôi có thể sử dụng teelệnh. Và tôi biết một chút về nó xargs, nhưng tôi không nghĩ đó là thứ tôi đang tìm kiếm.

Tôi muốn biết làm thế nào tôi có thể phân chia stdoutđể đi giữa hai (hoặc nhiều) lệnh. Ví dụ:

cat file.txt | stdout-split -c1 pbcopy -c2 grep -i errors

Có lẽ có một ví dụ tốt hơn ví dụ đó, nhưng tôi thực sự quan tâm đến việc làm thế nào tôi có thể gửi thiết bị xuất chuẩn đến một lệnh không chuyển tiếp nó và trong khi không stdoutbị "tắt tiếng" - Tôi không hỏi về cách gửi cattệp và grepmột phần của nó và sao chép nó vào clipboard - các lệnh cụ thể không quan trọng.

Ngoài ra - Tôi không hỏi làm thế nào để gửi nó vào một tệp và stdout- đây có thể là một câu hỏi "trùng lặp" (xin lỗi) nhưng tôi đã tìm một số và chỉ có thể tìm thấy những câu hỏi tương tự đang hỏi về cách phân chia giữa stdout và một tệp - và câu trả lời cho những câu hỏi đó dường như là tee, điều mà tôi không nghĩ sẽ có hiệu quả với tôi.

Cuối cùng, bạn có thể hỏi "tại sao không chỉ làm pbcopy là điều cuối cùng trong chuỗi ống?" và phản hồi của tôi là 1) nếu tôi muốn sử dụng nó mà vẫn thấy đầu ra trong giao diện điều khiển thì sao? 2) nếu tôi muốn sử dụng hai lệnh không xuất stdoutsau khi chúng xử lý đầu vào thì sao?

Ồ, và một điều nữa - Tôi nhận ra rằng tôi có thể sử dụng teevà một ống có tên ( mkfifo) nhưng tôi đã hy vọng một cách này có thể được thực hiện nội tuyến, chính xác, mà không cần thiết lập trước :)


Câu trả lời:


239

Bạn có thể sử dụng teevà xử lý thay thế cho việc này:

cat file.txt | tee >(pbcopy) | grep errors

Điều này sẽ gửi tất cả các đầu ra của cat file.txtđể pbcopy, và bạn sẽ chỉ nhận được kết quả của greptrên giao diện điều khiển của bạn.

Bạn có thể đặt nhiều quy trình trong teephần:

cat file.txt | tee >(pbcopy) >(do_stuff) >(do_more_stuff) | grep errors

21
Không phải là một mối quan tâm pbcopy, nhưng nói chung đáng nói: bất cứ điều gì đầu ra thay thế quá trình cũng được nhìn thấy bởi đoạn ống tiếp theo, sau đầu vào ban đầu; ví dụ: seq 3 | tee >(cat -n) | cat -e(đánh cat -nsố các dòng đầu vào, cat -eđánh dấu các dòng mới bằng $; bạn sẽ thấy cat -eđược áp dụng cho cả đầu vào ban đầu (đầu tiên) và (sau đó) đầu ra từ cat -n). Đầu ra từ nhiều sự thay thế quá trình sẽ đến theo thứ tự không xác định.
mkuity0

49
Các >(công trình duy nhất trong bash. Nếu bạn thử sử dụng ví dụ, shnó sẽ không hoạt động. Điều quan trọng là phải thông báo này.
AAlvz

10
@AAlvz: Điểm hay: thay thế quá trình không phải là tính năng POSIX; dash, hoạt động như shtrên Ubuntu, không hỗ trợ nó và ngay cả chính Bash cũng vô hiệu hóa tính năng này khi được gọi khi shhoặc khi set -o posixcó hiệu lực. Tuy nhiên, không chỉ Bash hỗ trợ thay thế quy trình: kshvà cũng zshhỗ trợ họ (không chắc chắn về người khác).
mkuity0

2
@ mkuity0 điều đó dường như không đúng. Trên zsh (Ubuntu 14.04), dòng của bạn in: 1 1 2 2 3 3 1 $ 2 $ 3 $ Thật đáng buồn, vì tôi thực sự muốn chức năng giống như bạn nói.
Aktau

2
@Aktau: Thật vậy, lệnh mẫu của tôi chỉ hoạt động như được mô tả trong bashksh- zshdường như không gửi đầu ra từ các thay thế quá trình đầu ra qua đường ống (có thể nói là tốt hơn , bởi vì nó không gây ô nhiễm cho những gì được gửi đến phân đoạn đường ống tiếp theo - mặc dù nó vẫn in ). Tuy nhiên, trong tất cả các hệ vỏ được đề cập, nói chung, không nên có một đường ống duy nhất trong đó đầu ra và đầu ra thông thường từ các thay thế quá trình được trộn lẫn - thứ tự đầu ra sẽ không thể dự đoán được, theo cách chỉ có thể xuất hiện không thường xuyên hoặc lớn bộ dữ liệu đầu ra.
mkuity0

124

Bạn có thể chỉ định nhiều tên tệp teevà ngoài ra, đầu ra tiêu chuẩn có thể được dẫn vào một lệnh. Để gửi đầu ra tới nhiều lệnh, bạn cần tạo nhiều ống và chỉ định mỗi ống là một đầu ra tee. Có nhiều hướng khác nhau để làm điều đó.

Quá trình thay thế

Nếu shell của bạn là ksh93, bash hoặc zsh, bạn có thể sử dụng quy trình thay thế. Đây là một cách để chuyển một đường ống đến một lệnh mong đợi một tên tệp. Shell tạo đường ống và chuyển một tên tệp như /dev/fd/3lệnh. Số là mô tả tập tin mà đường ống được kết nối. Một số biến thể unix không hỗ trợ /dev/fd; trên những cái này, một ống có tên được sử dụng thay thế (xem bên dưới).

tee >(command1) >(command2) | command3

Mô tả tập tin

Trong bất kỳ shell POSIX nào, bạn có thể sử dụng nhiều mô tả tệp một cách rõ ràng. Điều này đòi hỏi một biến thể unix hỗ trợ /dev/fd, vì tất cả trừ một trong những đầu ra teephải được chỉ định theo tên.

{ { { tee /dev/fd/3 /dev/fd/4 | command1 >&9;
    } 3>&1 | command2 >&9;
  } 4>&1 | command3 >&9;
} 9>&1

Đặt tên ống

Phương pháp cơ bản và di động nhất là sử dụng các đường ống có tên . Nhược điểm là bạn cần tìm một thư mục có thể ghi, tạo các đường ống và dọn dẹp sau đó.

tmp_dir=$(mktemp -d)
mkfifo "$tmp_dir/f1" "$tmp_dir/f2"
command1 <"$tmp_dir/f1" & pid1=$!
command2 <"$tmp_dir/f2" & pid2=$!
tee "$tmp_dir/f1" "$tmp_dir/f2" | command3
rm -rf "$tmp_dir"
wait $pid1 $pid2

10
Cảm ơn rất nhiều vì đã cung cấp hai phiên bản thay thế cho những người không muốn dựa vào bash hoặc một ksh nào đó.
trr

tee "$tmp_dir/f1" "$tmp_dir/f2" | command3Chắc chắn là command3 | tee "$tmp_dir/f1" "$tmp_dir/f2", như bạn muốn thiết bị xuất chuẩn của command3đường ống tee, phải không? Tôi đã thử nghiệm phiên bản của bạn bên dưới dashteechặn vô thời gian chờ đầu vào, nhưng việc chuyển đổi thứ tự tạo ra kết quả mong đợi.
Adrian Günter

1
@ AdrianGünter Không. Cả ba ví dụ đều đọc dữ liệu từ đầu vào tiêu chuẩn và gửi nó đến từng command, command2command3.
Gilles

@Gilles Tôi thấy, tôi đã hiểu sai ý định và cố gắng sử dụng đoạn mã không chính xác. Cảm ơn bạn đã làm rõ!
Adrian Günter

Nếu bạn không có quyền kiểm soát trên shell được sử dụng, nhưng bạn có thể sử dụng bash một cách rõ ràng, bạn có thể làm <command> | bash -c 'tee >(command1) >(command2) | command3'. Nó giúp trong trường hợp của tôi.
gc5

16

Chỉ cần chơi với sự thay thế quá trình.

mycommand_exec |tee >(grep ook > ook.txt) >(grep eek > eek.txt)

greplà hai nhị phân có cùng một đầu ra từ mycommand_execđầu vào cụ thể của quá trình.


16

Nếu bạn đang sử dụng zshthì bạn có thể tận dụng sức mạnh của MULTIOStính năng, tức là loại bỏ teehoàn toàn lệnh:

uname >file1 >file2

sẽ chỉ ghi đầu ra của unamehai tệp khác nhau: file1file2, tương đương vớiuname | tee file1 >file2

Tương tự chuyển hướng đầu vào tiêu chuẩn

wc -l <file1 <file2

tương đương với cat file1 file2 | wc -l(xin lưu ý rằng điều này không giống với wc -l file1 file2, sau này đếm số dòng trong mỗi tệp riêng biệt).

Tất nhiên, bạn cũng có thể sử dụng MULTIOSđể chuyển hướng đầu ra không đến các tệp mà đến các quy trình khác, sử dụng thay thế quy trình, ví dụ:

echo abc > >(grep -o a) > >(tr b x) > >(sed 's/c/y/')

3
Tốt để biết. MULTIOSlà một tùy chọn BẬT theo mặc định (và có thể tắt bằng unsetopt MULTIOS).
mkuity0

6

Đối với một đầu ra nhỏ hợp lý được tạo bởi một lệnh, chúng ta có thể chuyển hướng đầu ra sang tệp tạm thời và gửi các tệp tạm thời đó đến các lệnh trong vòng lặp. Điều này có thể hữu ích khi thứ tự các lệnh được thực thi có thể có vấn đề.

Kịch bản sau đây, ví dụ, có thể làm điều đó:

#!/bin/sh

temp=$( mktemp )
cat /dev/stdin > "$temp"

for arg
do
    eval "$arg" < "$temp"
done
rm "$temp"

Thử nghiệm chạy trên Ubuntu 16.04 với /bin/shnhư dashshell:

$ cat /etc/passwd | ./multiple_pipes.sh  'wc -l'  'grep "root"'                                                          
48
root:x:0:0:root:/root:/bin/bash

5

Nắm bắt lệnh STDOUTđến một biến và sử dụng lại bao nhiêu lần tùy thích:

commandoutput="$(command-to-run)"
echo "$commandoutput" | grep -i errors
echo "$commandoutput" | pbcopy

Nếu bạn cũng cần phải nắm bắt STDERR, sau đó sử dụng 2>&1ở cuối lệnh, như vậy:

commandoutput="$(command-to-run 2>&1)"

3
Các biến được lưu trữ ở đâu? Nếu bạn đang xử lý một tệp lớn hoặc một cái gì đó thuộc loại đó, liệu điều này có chiếm nhiều bộ nhớ không? Là các biến giới hạn về kích thước?
cwd

1
Điều gì xảy ra nếu $ Commandoutput là rất lớn?, tốt hơn là sử dụng đường ống và thay thế quy trình.
Nikhil Mulley

4
Rõ ràng giải pháp này chỉ khả thi khi bạn biết kích thước của đầu ra sẽ dễ dàng nằm gọn trong bộ nhớ và bạn vẫn ổn với việc đệm toàn bộ đầu ra trước khi chạy các lệnh tiếp theo trên nó. Các đường ống giải quyết hai vấn đề này bằng cách cho phép dữ liệu độ dài tùy ý và truyền dữ liệu theo thời gian thực tới người nhận khi nó được tạo.
trr

2
Đây là một giải pháp tốt nếu bạn có đầu ra nhỏ và bạn biết rằng đầu ra sẽ là văn bản và không phải là nhị phân. (các biến shell thường không an toàn nhị phân)
Rucent88 20/07/14

1
Tôi không thể làm điều này để làm việc với dữ liệu nhị phân. Tôi nghĩ đó là một cái gì đó với tiếng vang cố gắng diễn giải các byte rỗng hoặc một số dữ liệu không phải là vi khuẩn khác.
Rolf

1

Điều này có thể được sử dụng: http://www.spinellis.gr/sw/dgsh/ (vỏ đồ thị có hướng) Có vẻ như một sự thay thế bash hỗ trợ một cú pháp dễ dàng hơn cho các lệnh "đa bội".


0

Đây là một giải pháp một phần nhanh và bẩn, tương thích với mọi vỏ bao gồm busybox.

Vấn đề hẹp hơn mà nó giải quyết là: in toàn bộ stdoutsang một bàn điều khiển và lọc nó trên một bàn điều khiển khác, không có các tệp tạm thời hoặc các ống có tên.

  • Bắt đầu một phiên khác đến cùng một máy chủ. Để tìm ra tên TTY của nó, gõ tty. Hãy giả sử /dev/pty/2.
  • Trong phiên đầu tiên, hãy chạy the_program | tee /dev/pty/2 | grep ImportantLog:

Bạn nhận được một bản ghi đầy đủ và một bản ghi được lọc.

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.