Saya telah memipipkan baris dalam skrip bash dan ingin memeriksa apakah pipa tersebut memiliki data, sebelum memasukkannya ke suatu program.
Mencari yang saya temukan test -t 0
tetapi tidak berhasil di sini. Selalu mengembalikan false. Jadi bagaimana memastikan bahwa pipa itu memiliki data?
Contoh:
echo "string" | [ -t 0 ] && echo "empty" || echo "fill"
Keluaran: fill
echo "string" | tail -n+2 | [ -t 0 ] && echo "empty" || echo "fill"
Keluaran: fill
Tidak seperti standar / cara kanonik untuk menguji apakah sebelumnya pipa menghasilkan output? input perlu dipertahankan untuk meneruskannya ke program. Ini menggeneralisasi Bagaimana menyalurkan output dari satu proses ke proses lainnya tetapi hanya mengeksekusi jika yang pertama memiliki output? yang berfokus pada pengiriman email.
Jawaban:
Tidak ada cara untuk mengintip isi pipa menggunakan utilitas shell yang tersedia secara umum, juga tidak ada cara untuk membaca karakter ke pipa lalu memasangnya kembali. Satu-satunya cara untuk mengetahui bahwa pipa memiliki data adalah dengan membaca byte, dan kemudian Anda harus mendapatkan byte itu ke tujuannya.
Jadi lakukan saja: baca satu byte; jika Anda mendeteksi akhir file, maka lakukan apa yang ingin Anda lakukan ketika input kosong; jika Anda membaca byte maka garpu apa yang ingin Anda lakukan ketika input tidak kosong, pipa byte itu ke dalamnya, dan pipa sisa data.
The
ifne
utilitas dari MoreUtils Joey Hess menjalankan perintah jika input tidak kosong. Biasanya tidak diinstal secara default, tetapi harus tersedia atau mudah untuk membangun sebagian besar varian unix. Jika input kosong,ifne
tidak melakukan apa-apa dan mengembalikan status 0, yang tidak dapat dibedakan dari perintah yang berhasil dijalankan. Jika Anda ingin melakukan sesuatu jika inputnya kosong, Anda perlu mengatur agar perintah tidak mengembalikan 0, yang dapat dilakukan dengan meminta case sukses mengembalikan status kesalahan yang dapat dibedakan:test -t 0
tidak ada hubungannya dengan ini; ini menguji apakah input standar adalah terminal. Itu tidak mengatakan apa pun dengan satu atau lain cara, apakah ada input yang tersedia.sumber
peek
yang dapat mengembalikan data aktual dari sebuah pipa, tidak hanya seberapa banyak itu ada. (dalam 4.4 BSD, 386BSD dll. pipa diimplementasikan sebagai pasangan soket , tetapi itu memusnahkan dalam versi * BSD kemudian - meskipun mereka membuat mereka dua arah).read -t 0
(t dalam hal ini berarti batas waktu, jika Anda bertanya-tanya).Solusi sederhana adalah dengan menggunakan
ifne
perintah (jika input tidak kosong). Dalam beberapa distribusi, ini tidak diinstal secara default. Ini adalah bagian dari paketmoreutils
di sebagian besar distro.ifne
menjalankan perintah yang diberikan jika dan hanya jika input standar tidak kosongPerhatikan bahwa jika input standar tidak kosong, itu diteruskan
ifne
ke perintah yang diberikansumber
Pertanyaan lama, tetapi kalau-kalau ada orang yang menemukannya seperti saya: Solusi saya adalah membaca dengan batas waktu.
Jika
stdin
kosong, ini akan kembali setelah 5 detik. Kalau tidak, itu akan membaca semua input dan Anda dapat memprosesnya sesuai kebutuhan.sumber
-t
sayangnya bukan bagian dari POSIX: pubs.opengroup.org/onlinepubs/9699919799/utilities/read.htmlperiksa apakah file deskriptor stdin (0) terbuka atau tertutup:
sumber
[ -t 0 ]
memeriksa apakah fd 0 dibuka untuk tty , bukan apakah ditutup atau terbuka../that_script </dev/null
=> "stdin punya data". Atau./that_script <&-
agar stdin benar-benar tertutup .Anda dapat menggunakan
test -s /dev/stdin
(dalam subkulit eksplisit) juga.sumber
Dalam bash:
Mendeteksi jika input memiliki data (tanpa membaca apa pun). Kemudian Anda dapat membaca input (jika input tersedia pada saat membaca dijalankan):
Catatan: Memahami bahwa ini tergantung pada waktu. Ini mendeteksi jika input sudah memiliki data hanya pada saat
read -t
berjalan.Misalnya dengan
outputnya adalah
1
(baca kegagalan, yaitu: input kosong). Gema menulis beberapa data tetapi tidak terlalu cepat untuk memulai dan menulis byte pertama, dengan demikian,read -t 0
akan melaporkan bahwa inputnya kosong, karena program belum menulis apa pun.sumber
bash
apakah akan melakukanselect()
atau tidakioctl(FIONREAD)
, atau tidak keduanya, tetapi tidak keduanya, sebagaimana mestinya agar dapat berfungsi.read -t0
rusak. Jangan menggunakannyaSatu cara mudah untuk memeriksa apakah ada data yang tersedia untuk dibaca di Unix adalah dengan
FIONREAD
ioctl.Saya tidak dapat memikirkan utilitas standar melakukan hal itu, jadi di sini adalah program sepele melakukannya (lebih baik daripada
ifne
dari moreutils IMHO ;-)).Jika tidak ada data yang tersedia di stdin, itu akan keluar dengan status 1. Jika ada data, itu akan berjalan
prog
. Jika tidakprog
diberikan, itu akan keluar dengan status 0.Anda dapat menghapus
poll
panggilan jika Anda hanya tertarik pada data yang segera tersedia. Ini harus bekerja dengan sebagian besar jenis fds, bukan hanya pipa.fionread.c
sumber
POLLHUP
acara juga menangani kasing kosong? Apakah itu berfungsi jika ada beberapa deskripsi file di ujung pipa yang lain?Jika Anda suka one-liners pendek dan samar:
Saya menggunakan contoh-contoh dari pertanyaan awal. Jika Anda tidak ingin
-q
opsi penggunaan data yang disalurkan dengan grepsumber
Ini tampaknya menjadi implementasi ifne yang wajar di bash jika Anda setuju dengan membaca seluruh baris pertama
sumber
read
juga akan mengembalikan false jika inputnya tidak kosong tetapi tidak mengandung karakter baris baru,read
melakukan beberapa pemrosesan pada inputnya dan dapat membaca lebih dari satu baris kecuali Anda menyebutnya sebagaiIFS= read -r line
.echo
tidak dapat digunakan untuk data sewenang-wenang.Ini berfungsi untuk saya gunakan
read -rt 0
contoh dari pertanyaan awal, tanpa data:
sumber
{ sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }
(false negative) dantrue | { sleep .1; read -rt0 && echo YES; }
(false positive). Bahkan, bashread
akan tertipu bahkan oleh fds dibuka di write-satunya modus:{ read -rt0 && echo YES; cat; } 0>/tmp/foo
. Satu-satunya hal yang tampaknya dilakukan adalahselect(2)
pada fd itu.select
akan mengembalikan fd sebagai "siap" jika aread(2)
di atasnya tidak akan diblokir, tidak peduli apakah itu akan kembaliEOF
atau kesalahan. Kesimpulan:read -t0
yang rusak dibash
. Jangan gunakan itu.{ sleep .1; echo yes; } | { read -rt0 || echo NO; cat; }
bukan negatif palsu karena (pada saat membaca dijalankan) tidak ada input. Kemudian (tidur 0,1) input itu tersedia (untuk kucing).echo "" | { read -t0 && echo YES; }
mencetak YA tetapiecho "" | { read -rt0 && echo YES; }
tidak.