Chỉ đường ống STDERR qua bộ lọc


82

Có cách nào, trong bash, để chuyển STDERR qua một bộ lọc trước khi hợp nhất nó với STDOUT? Đó là, tôi muốn

STDOUT ────────────────┐
                       ├─────> terminal/file/whatever
STDERR ── [ filter ] ──┘

hơn là

STDOUT ────┐
           ├────[ filter ]───> terminal/file/whatever
STDERR ────┘

Câu trả lời:


64

Đây là một ví dụ, được mô phỏng theo cách hoán đổi các bộ mô tả tệp trong bash . Đầu ra của a.out là như sau, không có tiền tố 'STDXXX:'.

STDERR: stderr output
STDOUT: more regular

./a.out 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'
more regular
stdErr output

Trích dẫn từ liên kết trên:

  1. Đầu tiên hãy lưu stdout dưới dạng & 3 (& 1 được ghép vào 3)
  2. Tiếp theo, gửi stdout tới stderr (& 2 được ghép vào 1)
  3. Gửi stderr tới & 3 (stdout) (& 3 được ghép vào 2)
  4. đóng & 3 (& - được ghép vào 3)

1
Nó không hiệu quả với tôi. Cuối cùng tôi làm cho nó hoạt động với 3>&2 2>&1 1>&3-.
aleung

3
Thận trọng : điều này giả sử FD 3 không được sử dụng và không hoàn tác việc hoán đổi các bộ mô tả tệp 1 và 2, vì vậy bạn không thể tiếp tục chuyển lệnh này sang lệnh khác. Xem câu trả lời này để biết thêm chi tiết và giải quyết vấn đề. Để biết cú pháp rõ ràng hơn cho {ba, z} sh, hãy xem câu trả lời này .
Tom Hale

24

Việc sử dụng thay thế quy trình một cách ngây thơ dường như cho phép lọc stderr tách biệt khỏi stdout:

:; ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 )
out
e:err

Lưu ý rằng stderrđi ra vào stderrstdouttrên stdout, mà chúng ta có thể nhìn thấy bằng cách gói toàn bộ điều trong subshell khác và chuyển hướng đến các tập tin oe

( ( echo out ; echo err >&2 ) 2> >( sed s/^/e:/ >&2 ) ) 1>o 2>e

tại sao :; lúc bắt đầu? Tôi đã thử dòng ma thuật mà không có, và dường như không tạo ra sự khác biệt.
Koshmaar

1
Một số người sử dụng $như một dấu nhắc lệnh. Sau đó, họ thường viết ví dụ vỏ như: $ cat /var/log/syslog | fgrep .... Tuy nhiên, dòng này không thể sao chép được vì có $. :;trông giống như một lời nhắc nhưng về cơ bản là một no-op của trình bao; để bạn có thể chọn và dán toàn bộ dòng một cách an toàn.
solidnack

23
Được, nhưng bạn có thể bỏ qua:; và dòng cũng sẽ có thể sao chép được :) Đối với tôi:; không giống như một lời nhắc, nó không làm cho ví dụ rõ ràng hơn (tách các lệnh khỏi đầu ra) nhưng khó hiểu. Mặc dù tôi hiểu quan điểm của bạn và chúng ta không thảo luận về cú pháp / quy ước.
Koshmaar

24

TL; DR:

$ cmd 2> >(stderr-filter >&2)

Thí dụ:

% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%

Điều này sẽ hoạt động trong cả bash và zsh. Ngày nay, Bash khá phổ biến, tuy nhiên, nếu bạn thực sự cần một giải pháp (thực sự chính xác) cho POSIX sh, hãy xem tại đây .


Giải trình

Cho đến nay, cách dễ nhất để làm điều này là chuyển hướng STDERR thông qua thay thế quy trình :

Thay thế quy trình cho phép đầu vào hoặc đầu ra của quy trình được tham chiếu bằng cách sử dụng tên tệp. Nó có dạng

>(list)

Danh sách tiến trình được chạy không đồng bộ và đầu vào hoặc đầu ra của nó xuất hiện dưới dạng tên tệp.

Vì vậy, những gì bạn nhận được với quá trình thay thế là một tên tệp.

Giống như bạn có thể làm:

$ cmd 2> filename

bạn có thể làm

$ cmd 2> >(filter >&2)

Các >&2chuyển hướng của filterSTDOUT trở lại 's vào thiết bị lỗi chuẩn ban đầu.


1
Có một cảnh báo đi kèm với câu trả lời này: bash không đợi quá trình thay thế hoàn tất , trong khi FD tung hứng để hoán đổi 1 và 2 thì không. Điều này có thể quan trọng. Tôi đã bị đốt cháy bởi điều này làm exec 2> >(while .. read .. echo)trong một tập lệnh chạy như một dịch vụ systemd. journald chụp fd2 của dịch vụ và suy ra mức nhật ký từ tiền tố '<N>': thêm vào <2> các dòng đầu ra và bạn nhận được mức ERROR được gán cho các bản ghi tạp chí. Nhưng đôi khi systemd đã nhanh chóng giết toàn bộ nhóm quy trình khi thoát khỏi nhóm chính trước khi nó có thể ghi lại thông báo cuối cùng, thông điệp quan trọng nhất!
kkm

Giải pháp đơn giản tốt đẹp. Lưu ý: hãy cẩn thận khi sử dụng nó như một phần của công việc cron; quá trình thay thế không có sẵn trong một số shell được cron sử dụng.
Quinn Comendant

15

TL; DR: (bash và zsh)

$ cmd 2> >(stderr-filter >&2)

Thí dụ:

% cat /non-existant 2> >(tr o X >&2)
cat: /nXn-existant: NX such file Xr directXry
%

Nhiều câu trả lời trên mạng StackExchange có dạng:

cat /non-existant 3>&1 1>&2 2>&3 3>&- | sed 's/e/E/g'

Điều này có một giả định tích hợp: bộ mô tả tệp 3 không được sử dụng cho việc khác.

Thay vào đó, hãy sử dụng bộ mô tả tệp được đặt tên và {ba,z}shsẽ cấp phát bộ mô tả tệp có sẵn tiếp theo> = 10:

cat /non-existant {tmp}>&1 1>&2 2>&$tmp {tmp}>&- | sed 's/e/E/g'

Lưu ý rằng bộ mô tả tệp được đặt tên không được hỗ trợ bởi POSIX sh .

Vấn đề khác ở trên là lệnh không thể được chuyển sang các lệnh khác mà không hoán đổi lại STDOUT và STDERR trở lại giá trị ban đầu của chúng.

Để cho phép đường ống chuyển tiếp trong POSIX sh, (và vẫn giả sử FD 3 không được sử dụng), nó sẽ phức tạp :

(cmd 2>&1 >&3 3>&- | stderr-filter >&2 3>&-) 3>&1

Vì vậy, với giả định và cú pháp gnarly của điều này, bạn có thể tốt hơn nên sử dụng cú pháp bash/ đơn giản hơn zshđược hiển thị trong TL; DR ở trên và được giải thích ở đây .


8

Tôi thấy việc sử dụng thay thế quy trình bash dễ nhớ và dễ sử dụng hơn vì nó phản ánh ý định ban đầu gần như nguyên văn. Ví dụ:

$ cat ./p
echo stdout
echo stderr >&2
$ ./p 2> >(sed -e 's/s/S/') | sed 's/t/T/'
sTdout
STderr

sử dụng lệnh sed đầu tiên làm bộ lọc chỉ trên stderr và lệnh sed thứ hai để sửa đổi đầu ra đã kết hợp.

Lưu ý rằng khoảng trắng sau 2> là bắt buộc để lệnh được phân tích cú pháp chính xác.


5

Phần cuối cùng của trang này của Hướng dẫn Viết kịch bản Bash Nâng cao là "chỉ chuyển hướng stderr tới một đường ống".

# Chỉ chuyển hướng stderr đến một đường ống.

exec 3>&1                              # Save current "value" of stdout.
ls -l 2>&1 >&3 3>&- | grep bad 3>&-    # Close fd 3 for 'grep' (but not 'ls').
#              ^^^^   ^^^^
exec 3>&-                              # Now close it for the remainder of the script.

# Cảm ơn, SC

Đây có thể là những gì bạn muốn. Nếu không, một số bộ phận khác của ABSG sẽ có thể giúp bạn, điều đó thật tuyệt vời.


Chúng tôi hơi do dự khi đề xuất ABSG như một tài liệu tham khảo, vì nó trộn lẫn tài liệu, đơn thuốc và ý kiến ​​mà không đánh dấu rõ ràng sự khác biệt. Một số phần cũng có nội dung đáng ngờ, mặc dù phần bạn liên kết đến có vẻ ổn.
tripleee

3

Hãy xem các đường ống được đặt tên:

$ mkfifo err
$ cmd1 2>err |cat - err |cmd2

sẽ không cat - errphá vỡ sự xen kẽ của stdout và stderr?
Martin DeMello

@Martin - nó phụ thuộc. Nếu bộ đệm cmd1, cat hoặc cmd2 xuất ra, thì bạn có thể thấy đầu ra không theo trình tự.
mob
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.