it-swarm-vi.com

Làm thế nào để grep luồng lỗi tiêu chuẩn (stderr)?

Tôi đang sử dụng ffmpeg để lấy thông tin meta của clip âm thanh. Nhưng tôi không thể grep nó.

    $ ffmpeg -i 01-Daemon.mp3  |grep -i Duration
    FFmpeg version SVN-r15261, Copyright (c) 2000-2008 Fabrice Bellard, et al.
      configuration: --prefix=/usr --bindir=/usr/bin 
      --datadir=/usr/share/ffmpeg --incdir=/usr/include/ffmpeg --libdir=/usr/lib
      --mandir=/usr/share/man --Arch=i386 --extra-cflags=-O2 
      ...

Tôi đã kiểm tra, đầu ra ffmpeg này được chuyển đến stderr.

$ ffmpeg -i 01-Daemon.mp3 2> /dev/null

Vì vậy, tôi nghĩ rằng grep không thể đọc luồng lỗi để bắt các dòng khớp. Làm thế nào chúng ta có thể kích hoạt grep để đọc luồng lỗi?

Sử dụng liên kết nixCraft , tôi đã chuyển hướng luồng lỗi tiêu chuẩn sang luồng đầu ra tiêu chuẩn, sau đó grep hoạt động.

$ ffmpeg -i 01-Daemon.mp3 2>&1 | grep -i Duration
  Duration: 01:15:12.33, start: 0.000000, bitrate: 64 kb/s

Nhưng điều gì sẽ xảy ra nếu chúng ta không muốn chuyển hướng thiết bị xuất chuẩn sang thiết bị xuất chuẩn?

90
Andrew-Dufresne

Nếu bạn đang sử dụng bash tại sao không sử dụng các đường ống ẩn danh, thực chất là viết tắt cho những gì phunehehe nói:

ffmpeg -i 01-Daemon.mp3 2> >(grep -i Duration)

66
Jé Queue

Không có lớp vỏ thông thường nào (thậm chí zsh) cho phép các đường ống khác ngoài từ stdout đến stdin. Nhưng tất cả các shell kiểu Bourne đều hỗ trợ gán lại mô tả tệp (như trong 1>&2). Vì vậy, bạn có thể tạm thời chuyển hướng thiết bị xuất chuẩn sang fd 3 và stderr sang thiết bị xuất chuẩn, và sau đó đưa fd 3 trở lại thiết bị xuất chuẩn. Nếu stuff tạo ra một số đầu ra trên thiết bị xuất chuẩn và một số đầu ra trên thiết bị xuất chuẩn và bạn muốn áp dụng filter trên đầu ra lỗi mà không chạm đến đầu ra tiêu chuẩn, bạn có thể sử dụng { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1.

$ stuff () {
  echo standard output
  echo more output
  echo standard error 1>&2
  echo more error 1>&2
}
$ filter () {
  grep a
}
$ { stuff 2>&1 1>&3 | filter 1>&2; } 3>&1
standard output
more output
standard error
49

Điều này tương tự như "thủ thuật tập tin tạm thời" của phunehehe, nhưng sử dụng một đường ống có tên thay vào đó, cho phép bạn có được kết quả gần hơn một chút khi chúng được xuất ra, có thể thuận tiện cho các lệnh chạy dài:

$ mkfifo mypipe
$ command 2> mypipe | grep "pattern" mypipe

Trong cấu trúc này, stderr sẽ được dẫn đến đường ống có tên "mypipe". Vì grep đã được gọi với một đối số tệp, nên nó sẽ không tìm đến STDIN cho đầu vào của nó. Thật không may, bạn vẫn sẽ phải dọn sạch đường ống có tên đó sau khi bạn hoàn thành.

Nếu bạn đang sử dụng Bash 4, có một cú pháp phím tắt cho command1 2>&1 | command2, đó là command1 |& command2. Tuy nhiên, tôi tin rằng đây hoàn toàn là một phím tắt cú pháp, bạn vẫn đang chuyển hướng STDERR sang STDOUT.

22
Steven D

Câu trả lời của Gilles và Stefan Lasiewski đều tốt, nhưng cách này đơn giản hơn:

ffmpeg -i 01-Daemon.mp3 2>&1 >/dev/null | grep "pattern"

Tôi cho rằng bạn không muốn ffmpeg's thiết bị xuất chuẩn.

Làm thế nào nó hoạt động:

  • ống đầu tiên [.__.]
    • ffmpeg và grep được bắt đầu, với thiết bị xuất chuẩn của ffmpeg sẽ chuyển sang stdin của grep
  • chuyển hướng tiếp theo, từ trái sang phải [.__.]
    • stderr của ffmpeg được đặt thành bất kể thiết bị xuất chuẩn của nó là gì (hiện tại là đường ống)
    • thiết bị xuất chuẩn của ffmpeg được đặt thành/dev/null
11
Mikel

Xem dưới đây cho các kịch bản được sử dụng trong các thử nghiệm này.

Grep chỉ có thể hoạt động trên stdin, do đó bạn phải chuyển đổi luồng stderr theo hình thức mà Grep có thể phân tích cú pháp.

Thông thường, stdout và stderr đều được in ra màn hình của bạn:

$ ./stdout-stderr.sh
./stdout-stderr.sh: Printing to stdout
./stdout-stderr.sh: Printing to stderr

Để ẩn stdout, nhưng vẫn in stderr làm điều này:

$ ./stdout-stderr.sh >/dev/null
./stdout-stderr.sh: Printing to stderr

Nhưng grep sẽ không hoạt động trên stderr! Bạn sẽ mong đợi lệnh sau để chặn các dòng có chứa 'err', nhưng không được.

$ ./stdout-stderr.sh >/dev/null |grep --invert-match err
./stdout-stderr.sh: Printing to stderr

Đây là giải pháp.

Cú pháp Bash sau sẽ ẩn đầu ra thành thiết bị xuất chuẩn, nhưng vẫn sẽ hiển thị stderr. Đầu tiên chúng ta chuyển stdout thành/dev/null, sau đó chúng ta chuyển stderr thành stdout, bởi vì các ống Unix sẽ chỉ hoạt động trên thiết bị xuất chuẩn. Bạn vẫn có thể grep văn bản.

$ ./stdout-stderr.sh 2>&1 >/dev/null | grep err
./stdout-stderr.sh: Printing to stderr

(Lưu ý rằng lệnh trên là khác nha thì ./command >/dev/null 2>&1, đó là một lệnh rất phổ biến).

Đây là kịch bản được sử dụng để thử nghiệm. Điều này in một dòng tới thiết bị xuất chuẩn và một dòng cho thiết bị xuất chuẩn:

#!/bin/sh

# Print a message to stdout
echo "$0: Printing to stdout"
# Print a message to stderr
echo "$0: Printing to stderr" >&2

exit 0
8
Stefan Lasiewski

Bạn có thể trao đổi các luồng. Điều này sẽ cho phép bạn grep luồng lỗi tiêu chuẩn ban đầu trong khi vẫn nhận được đầu ra ban đầu đi đến đầu ra tiêu chuẩn trong thiết bị đầu cuối:

somecommand 3>&2 2>&1 1>&3- | grep 'pattern'

Điều này hoạt động bằng cách trước tiên tạo một bộ mô tả tệp mới (3) mở cho đầu ra và đặt nó vào luồng lỗi tiêu chuẩn (3>&2). Sau đó, chúng tôi chuyển hướng lỗi tiêu chuẩn sang đầu ra tiêu chuẩn (2>&1). Cuối cùng, đầu ra tiêu chuẩn được chuyển hướng đến lỗi tiêu chuẩn ban đầu và bộ mô tả tệp mới được đóng (1>&3-).

Trong trường hợp của bạn:

ffmpeg -i 01-Daemon.mp3 3>&2 2>&1 1>&3- | grep -i Duration

Kiểm tra nó:

$ ( echo "error" >&2; echo "output" ) 3>&2 2>&1 1>&3- | grep "error"
output
error

$ ( echo "error" >&2; echo "output" ) 3>&2 2>&1 1>&3- | grep -v "error"
output
3
Kusalananda

Khi bạn chuyển đầu ra của lệnh này sang lệnh khác (sử dụng |), bạn chỉ chuyển hướng đầu ra tiêu chuẩn. Vì vậy, điều đó sẽ giải thích tại sao

ffmpeg -i 01-Daemon.mp3 | grep -i Duration

không xuất ra những gì bạn muốn (mặc dù nó hoạt động).

Nếu bạn không muốn chuyển hướng đầu ra lỗi sang đầu ra tiêu chuẩn, bạn có thể chuyển hướng đầu ra lỗi sang một tệp, sau đó grep nó sau

ffmpeg -i 01-Daemon.mp3 2> /tmp/ffmpeg-error
grep -i Duration /tmp/ffmpeg-error
3
phunehehe

bash có thể chuyển hướng stdout sang luồng stdin thông qua đường ống thông thường - | Nó cũng có thể chuyển hướng cả stdout và stderr sang stdin bởi |&

1
0x00

Tôi thích sử dụng rc Shell trong trường hợp này.

Đầu tiên cài đặt gói (nó ít hơn 1MB).

Đây là một ví dụ về cách bạn sẽ loại bỏ stdout và pipe stderr thành grep trong rc:

find /proc/ >[1] /dev/null |[2] grep task

Bạn có thể làm điều đó mà không cần rời Bash:

rc -c 'find /proc/ >[1] /dev/null |[2] grep task'

Như bạn có thể nhận thấy, cú pháp rất đơn giản, điều này làm cho giải pháp ưa thích của tôi.

Bạn có thể chỉ định mô tả tệp nào bạn muốn đặt trong ngoặc, ngay sau ký tự ống.

Các mô tả tệp tiêu chuẩn được đánh số như sau:

  • 0: Đầu vào tiêu chuẩn
  • 1: Tiêu chuẩn
  • 2: Lỗi tiêu chuẩn
1
Rolf

Một biến thể trên ví dụ về quy trình con bash:

lặp lại hai dòng để stderr và tee stderr vào một tập tin, grep tee và pipe trở lại stdout

(>&2 echo -e 'asdf\nfff\n') 2> >(tee some.load.errors | grep 'fff' >&1)

xuất sắc:

fff

some.load.errors (vd: stderr):

asdf
fff
0
jmunsch