Làm một đường ống có điều kiện trên lợi nhuận không trống


16

Có cách nào để ống đầu ra của một chức năng khác, nhưng chỉ nếu có một đầu ra?


Câu trả lời:


9

Hàm sau đây ifnotemptyđưa đầu vào của nó vào lệnh được truyền dưới dạng đối số, ngoại trừ việc nó không làm gì nếu đầu vào trống. Sử dụng nó để dẫn source --foovào sink --barbằng cách viết source --foo | pipe_if_not_empty sink --bar.

pipe_if_not_empty () {
  head=$(dd bs=1 count=1 2>/dev/null; echo a)
  head=${head%a}
  if [ "x$head" != x"" ]; then
    { printf %s "$head"; cat; } | "$@"
  fi
}

Ghi chú thiết kế:

  • Tôi hy vọng việc triển khai này sẽ hoạt động trên tất cả các nền tảng POSIX / Unix, mặc dù nói đúng ra nó không tuân thủ tiêu chuẩn: nó phụ thuộc vào việc ddkhông đọc nhiều hơn một byte mà nó được cho là đọc trên đầu vào tiêu chuẩn của nó.
  • Tôi nghĩ rằng head -c 1nó sẽ là một sự thay thế phù hợp cho dd bs=1 count=1 2>/dev/nullLinux.
  • Mặt khác, head -n 1sẽ không phù hợp vì headthông thường đệm đầu vào của nó và có thể đọc nhiều hơn một dòng mà nó xuất ra - và vì nó đọc từ một đường ống, các byte thừa sẽ bị mất.
  • read -r headvà thậm chí read -r -n 1 headkhông phù hợp ở đây vì nếu ký tự đầu tiên là một dòng mới, headsẽ được đặt thành chuỗi trống, khiến cho không thể phân biệt giữa đầu vào trống và đầu vào bắt đầu bằng một dòng trống.
  • Chúng ta không thể viết head=$(head -c 1)bởi vì nếu ký tự đầu tiên là một dòng mới, việc thay thế lệnh sẽ loại bỏ dòng mới cuối cùng, khiến cho không thể phân biệt giữa đầu vào trống và đầu vào bắt đầu bằng một dòng trống.
  • Trong bash, ksh hoặc zsh, bạn có thể thay thế catbằng cách </dev/stdintăng hiệu suất vi mô.

Nếu bạn không lưu trữ toàn bộ dữ liệu trung gian trong bộ nhớ, thì đây là cách thực hiện đơn giản hơn một chút pipe_if_not_empty.

pipe_if_not_empty () {
  input=$(cat; echo a);
  if [ "x$input" != x"a" ]; then
    { printf %s "${input%a}"; } | "$@"
  fi
}

Đây là một cách thực hiện đơn giản hơn với các cảnh báo sau:

  • Dữ liệu do nguồn tạo ra được coi là trống khi và chỉ khi nó chỉ bao gồm các ký tự dòng mới. (Điều này trong thực tế có thể là mong muốn.)
  • Dữ liệu được đưa vào bồn kết thúc với chính xác một ký tự dòng mới, bất kể có bao nhiêu dòng mới mà dữ liệu được tạo bởi nguồn kết thúc bằng. (Đây có thể là một vấn đề.)

Một lần nữa, toàn bộ dữ liệu được lưu trữ trong bộ nhớ.

pipe_if_not_empty () {
  input=$(cat);
  if [ "x$input" != x"" ]; then
    { printf '%s\n' "${input}"; } | "$@"
  fi
}

17

Điều này sẽ làm việc cho bạn

$ --a function-- | [ xargs -r ] --another function--

Một ví dụ

$ echo -e "\n\n" | xargs -r ls
$ # No output. ls did not run.
$ echo -e "\n\n1" | xargs -r ls
ls: cannot access 1: No such file or directory

Nó đơn giản nhưng nó sẽ làm việc cho bạn. Nếu "một chức năng" của bạn gửi một chuỗi trống hoặc thậm chí một dòng mới xuống đường ống, xargs -r, sẽ ngăn việc chuyển qua "chức năng khác".

Tham khảo cho xargs: http://www.oreillynet.com/linux/cmd/cmd.csp?path=x/xargs

-r, --no-run-if-empty
Do not run command if standard input contains only blanks.

9

ifne (1) từ moreutils thực hiện chính xác điều đó. Moreutils có sẵn dưới dạng một gói ít nhất là trong Debian và Ubuntu, có lẽ trong các bản phát hành khác.


4

Hàm bên dưới cố gắng đọc byte thứ 1 và nếu thành công tiếng vang đó là byte và cat phần còn lại. Nên hiệu quả và di động 100%.

if_read() {
    IFS="" read -rN 1 BYTE && { echo -nE "$BYTE"; cat; } | "$@";
}

Các trường hợp thử nghiệm:

$ echo -n | if_read wc -c
$ echo | if_read wc -c
1
$ echo -en "\nX" | if_read wc -c
2
$

Chức năng này thất bại đối với tôi khi tôi chạy echo -en "\nX" | pipe_if_not_empty mail -s "Subject line here" foo@bar.com. Nó nghĩ rằng lineherecả hai đều là người nhận e-mail, không phải là mã thông báo trong chủ đề. Tôi phải thoát khỏi "chủ đề xung quanh để khiến nó hoạt động. Tuy nhiên, pipe_if_not_emptychức năng từ câu trả lời được chấp nhận hoạt động với tôi ngay cả khi không thoát khỏi bất cứ điều gì.
kuzzooroo

2

Ít nhất một cái gì đó như thế này hoạt động:

yourcommand | if [ $(wc -c) -gt "0" ]; then yourothercommand; fi

Xin lưu ý rằng ở trên sẽ coi các nguồn cấp dữ liệu và các ký tự đặc biệt khác làm đầu ra, do đó, một dòng trống được truyền cho câu lệnh if đó sẽ được coi là đầu ra. Chỉ cần tăng giới hạn -gt nếu đầu ra của bạn thường phải cao hơn 1 byte :)


yourothercommandkhông bao giờ thấy đầu ra của yourcommand.
Tạm dừng cho đến khi có thông báo mới.

2

Thay vì sender | receiver:

tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | receiver; fi; }
sender | tester

Hoặc bạn có thể làm cho mục đích chung hơn bằng cách thay đổi nó để chấp nhận chương trình nhận dưới dạng đối số như trong câu trả lời của Gilles:

tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | "$@"; fi; }
sender | tester receiver
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.