Có cách nào để ống đầu ra của một chức năng khác, nhưng chỉ nếu có là một đầu ra?
Có cách nào để ống đầu ra của một chức năng khác, nhưng chỉ nếu có là một đầu ra?
Câu trả lời:
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 --foo
vào sink --bar
bằ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ế:
dd
khô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ó.head -c 1
nó sẽ là một sự thay thế phù hợp cho dd bs=1 count=1 2>/dev/null
Linux.head -n 1
sẽ không phù hợp vì head
thô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 head
và thậm chí read -r -n 1 head
không phù hợp ở đây vì nếu ký tự đầu tiên là một dòng mới, head
sẽ đượ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.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.cat
bằng cách </dev/stdin
tă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:
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
}
Đ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.
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
$
echo -en "\nX" | pipe_if_not_empty mail -s "Subject line here" foo@bar.com
. Nó nghĩ rằng line
và here
cả 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_empty
chứ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ì.
Í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 :)
yourothercommand
không bao giờ thấy đầu ra của yourcommand
.
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