Buat pipa bersyarat pada pengembalian yang tidak kosong

Jawaban:

9

Fungsi berikut ifnotemptymengirimkan inputnya ke perintah yang diteruskan sebagai argumen, kecuali bahwa ia tidak melakukan apa-apa jika inputnya kosong. Gunakan untuk pipa source --fooke dalam sink --bardengan 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:

  • Saya berharap implementasi ini bekerja pada semua platform POSIX / Unix, meskipun secara tegas itu tidak sesuai standar: ia bergantung pada ddtidak membaca lebih dari satu byte yang diperintahkan untuk dibaca pada input standarnya.
  • Saya pikir head -c 1akan menjadi pengganti yang cocok untuk dd bs=1 count=1 2>/dev/nulldi Linux.
  • Di sisi lain, head -n 1tidak akan cocok karena headbiasanya buffer input dan dapat membaca lebih dari satu baris yang dihasilkan - dan karena itu membaca dari pipa, byte tambahan hilang begitu saja.
  • read -r headdan bahkan read -r -n 1 headtidak cocok di sini karena jika karakter pertama adalah baris baru, headakan ditetapkan ke string kosong, sehingga tidak mungkin untuk membedakan antara input kosong dan input dimulai dengan baris kosong.
  • Kita tidak bisa hanya menulis 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.
  • Di bash, ksh atau zsh, Anda bisa menggantinya catdengan </dev/stdinuntuk 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:

  • Data yang dihasilkan oleh sumber dianggap kosong jika dan hanya jika itu hanya terdiri dari karakter baris baru. (Ini sebenarnya mungkin diinginkan.)
  • Data dimasukkan ke wastafel berakhir dengan tepat satu karakter baris baru, tidak peduli berapa banyak baris baru data yang dihasilkan oleh sumber berakhir. (Ini bisa jadi masalah.)

Sekali lagi, seluruh data disimpan dalam memori.

pipe_if_not_empty () {
  input=$(cat);
  if [ "x$input" != x"" ]; then
    { printf '%s\n' "${input}"; } | "$@"
  fi
}
Gilles 'SANGAT berhenti menjadi jahat'
sumber
17

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.
Randy Skretka
sumber
9

ifne (1) dari moreutils melakukan hal itu. Moreutils tersedia sebagai paket setidaknya di Debian dan Ubuntu, mungkin di distro lain juga.

Jukka Matilainen
sumber
4

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
$
Benjamin Polak
sumber
Fungsi ini gagal untuk saya ketika saya menjalankan echo -en "\nX" | pipe_if_not_empty mail -s "Subject line here" [email protected]. Ia berpikir bahwa linedan herekeduanya adalah penerima email, bukan token dalam subjek. Saya harus melarikan diri dari "sekitar subjek untuk membuatnya bekerja. Namun, pipe_if_not_emptyfungsi dari jawaban yang diterima bekerja untuk saya bahkan tanpa melarikan diri apa pun.
kuzzooroo
2

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 :)

Janne Pikkarainen
sumber
yourothercommandtidak pernah melihat output dari yourcommand.
Dijeda sampai pemberitahuan lebih lanjut.
2

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
Dijeda sampai pemberitahuan lebih lanjut.
sumber