Apakah ada cara untuk pipa output dari satu fungsi ke yang lain, tetapi hanya jika ada adalah output?
Apakah ada cara untuk pipa output dari satu fungsi ke yang lain, tetapi hanya jika ada adalah output?
Jawaban:
Fungsi berikut ifnotempty
mengirimkan inputnya ke perintah yang diteruskan sebagai argumen, kecuali bahwa ia tidak melakukan apa-apa jika inputnya kosong. Gunakan untuk pipa source --foo
ke dalam sink --bar
dengan menulis 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
}
Catatan desain:
dd
tidak membaca lebih dari satu byte yang diperintahkan untuk dibaca pada input standarnya.head -c 1
akan menjadi pengganti yang cocok untuk dd bs=1 count=1 2>/dev/null
di Linux.head -n 1
tidak akan cocok karena head
biasanya buffer input dan dapat membaca lebih dari satu baris yang dihasilkan - dan karena itu membaca dari pipa, byte tambahan hilang begitu saja.read -r head
dan bahkan read -r -n 1 head
tidak cocok di sini karena jika karakter pertama adalah baris baru, head
akan ditetapkan ke string kosong, sehingga tidak mungkin untuk membedakan antara input kosong dan input dimulai dengan baris kosong.head=$(head -c 1)
karena jika karakter pertama adalah baris baru, substitusi perintah akan menghapus baris baru akhir, sehingga tidak mungkin untuk membedakan antara input kosong dan input yang dimulai dengan baris kosong.cat
dengan </dev/stdin
untuk mendapatkan kinerja mikroskopis.Jika Anda tidak keberatan menyimpan seluruh data antara dalam memori, berikut ini adalah implementasi yang sedikit lebih sederhana pipe_if_not_empty
.
pipe_if_not_empty () {
input=$(cat; echo a);
if [ "x$input" != x"a" ]; then
{ printf %s "${input%a}"; } | "$@"
fi
}
Berikut ini adalah implementasi yang sedikit lebih sederhana dengan peringatan berikut:
Sekali lagi, seluruh data disimpan dalam memori.
pipe_if_not_empty () {
input=$(cat);
if [ "x$input" != x"" ]; then
{ printf '%s\n' "${input}"; } | "$@"
fi
}
Ini seharusnya bekerja untuk Anda
$ --a function-- | [ xargs -r ] --another function--
Sebuah contoh
$ 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
Ini sederhana tetapi harus bekerja untuk Anda. Jika "fungsi" Anda mengirim string kosong atau bahkan baris baru ke dalam pipa, xargs -r, akan mencegah lewatnya "fungsi lain".
Referensi untuk 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.
Fungsi di bawah ini mencoba untuk membaca byte pertama, dan jika berhasil gema byte itu dan sisanya kucing. Harus efisien dan 100% portabel.
if_read() {
IFS="" read -rN 1 BYTE && { echo -nE "$BYTE"; cat; } | "$@";
}
Kasus uji:
$ 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
. Ia berpikir bahwa line
dan here
keduanya adalah penerima email, bukan token dalam subjek. Saya harus melarikan diri dari "
sekitar subjek untuk membuatnya bekerja. Namun, pipe_if_not_empty
fungsi dari jawaban yang diterima bekerja untuk saya bahkan tanpa melarikan diri apa pun.
Setidaknya sesuatu seperti ini berfungsi:
yourcommand | if [ $(wc -c) -gt "0" ]; then yourothercommand; fi
Harap dicatat bahwa di atas akan mempertimbangkan umpan baris dan karakter khusus lainnya sebagai output, sehingga baris kosong diteruskan ke jika pernyataan akan dianggap sebagai output. Cukup naikkan batas -gt jika output Anda biasanya harus lebih tinggi dari 1 byte :)
yourothercommand
tidak pernah melihat output dari yourcommand
.
Alih-alih sender | receiver
:
tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | receiver; fi; }
sender | tester
Atau Anda bisa menjadikannya lebih umum dengan mengubahnya untuk menerima program penerima sebagai argumen seperti dalam jawaban Gilles:
tester () { local a=$(</dev/stdin); if [[ $a ]]; then printf '%s\n' "$a" | "$@"; fi; }
sender | tester receiver