Bagaimana tidak melakukan apa pun selamanya dengan cara yang elegan?

82

Saya memiliki program yang menghasilkan informasi bermanfaat stdouttetapi juga membaca dari stdin. Saya ingin mengarahkan output standarnya ke file tanpa memberikan apa pun pada input standar. Sejauh ini, sangat bagus: Saya bisa melakukan:

program > output

dan jangan melakukan apa pun di tty.

Namun, masalahnya adalah saya ingin melakukan ini di latar belakang. Jika aku melakukan:

program > output &

program akan ditangguhkan ("ditangguhkan (tty input)").

Jika aku melakukan:

program < /dev/null > output &

program berakhir segera karena mencapai EOF.

Tampaknya apa yang saya butuhkan adalah menyalurkan ke programsesuatu yang tidak melakukan apa pun untuk waktu yang tidak terbatas dan tidak membaca stdin. Pendekatan berikut bekerja:

while true; do sleep 100; done | program > output &
mkfifo fifo && cat fifo | program > output &
tail -f /dev/null | program > output &

Namun, ini semua sangat jelek. Ada memiliki menjadi cara yang elegan, menggunakan standar utilitas Unix, untuk "melakukan apa-apa, tanpa batas" (untuk parafrase man true). Bagaimana saya bisa mencapai ini? (Kriteria utama saya untuk keanggunan di sini: tidak ada file sementara; tidak ada sibuk menunggu atau bangun berkala; tidak ada utilitas eksotis; sesingkat mungkin.)

a3nm
sumber
Coba su -c 'program | output &' user. Saya akan mengajukan pertanyaan serupa dengan menciptakan pekerjaan latar belakang sebagai metode yang dapat diterima untuk menangani "layanan / daemon." Saya juga memperhatikan bahwa saya tidak bisa mengarahkan ulang STDERRtanpa juga mengarahkan ulang STDOUT. Solusi tempat programA mengirim STDOUTke STDINprogramB, lalu mengalihkan STDERRke file log:programA 2> /var/log/programA.log | programB 2> /var/log/programB.log 1> /dev/null
mbrownnyc
mungkin ... su -c 'while true; do true; done | cat > ~/output &' user?
mbrownnyc
program apa itu?
João Portela
João Portela: Ini adalah program yang saya tulis, gitorious.org/irctk
a3nm
Mengapa tidak sekadar menambahkan peralihan ke program yang Anda tulis? Juga, saya berasumsi bahwa jika Anda menutup stdin dengan 1<&-itu akan keluar dari program Anda?
w00t

Jawaban:

16

Dalam shell yang mendukungnya (ksh, zsh, bash4), Anda bisa mulai programsebagai proses bersama .

  • ksh: program > output |&
  • zsh, bash:coproc program > output

Itu dimulai programdi latar belakang dengan inputnya dialihkan dari a pipe. Ujung pipa lainnya terbuka ke shell.

Tiga manfaat dari pendekatan itu

  • tidak ada proses ekstra
  • Anda dapat keluar dari skrip saat programmati (gunakan waituntuk menunggu)
  • programakan berakhir (dapatkan eofstdin-nya jika shell keluar).
Stéphane Chazelas
sumber
Itu sepertinya bekerja dan terlihat seperti ide bagus! (Agar adil, saya telah meminta sesuatu untuk dikirim ke perintah saya, bukan untuk fitur shell, tapi ini hanya masalah XY yang sedang dimainkan.) Saya mempertimbangkan untuk menerima jawaban ini alih-alih sebagai salah satu dari @ PT.
a3nm
1
@ a3nm, tail -f /dev/nulltidak ideal karena membaca setiap detik aktif /dev/null(versi terbaru dari GNU tail di Linux menggunakan inotify sebenarnya ada bug ). sleep infatau yang lebih portabel setara sleep 2147483647adalah pendekatan yang lebih baik untuk perintah yang duduk di sana tidak melakukan IMO (catatan yang sleepdibangun dalam beberapa shell seperti ksh93atau mksh).
Stéphane Chazelas
78

Saya tidak berpikir Anda akan mendapatkan yang lebih elegan dari pada

tail -f /dev/null

Anda sudah menyarankan (dengan asumsi ini menggunakan inotify internal, seharusnya tidak ada polling atau bangun, jadi selain terlihat aneh, itu harus cukup).

Anda memerlukan utilitas yang akan berjalan tanpa batas waktu, akan tetap membuka stdout, tetapi tidak akan benar-benar menulis apa pun untuk stdout, dan tidak akan keluar ketika stdin ditutup. Sesuatu seperti yesmenulis di stdout. catakan keluar ketika stdinnya ditutup (atau apa pun yang Anda arahkan kembali ke dalamnya sudah selesai). Saya pikir sleep 1000000000dmungkin berhasil, tetapi tailjelas lebih baik. Kotak Debian saya memiliki tailfperintah yang mempersingkat sedikit.

Mengambil taktik berbeda, bagaimana menjalankan program di bawah screen?

PT
sumber
Saya suka tail -f /dev/nullpendekatan yang terbaik dan merasa cukup elegan juga, karena penggunaan perintah cocok dengan tujuan yang dimaksud dengan sangat dekat.
jw013
2
Dari strace tail -f /dev/nulltampaknya yang tailmenggunakan inotifydan bahwa bangun terjadi dalam kasus konyol seperti sudo touch /dev/null. Sangat menyedihkan bahwa tampaknya tidak ada solusi yang lebih baik ... Saya ingin tahu yang mana syscall yang tepat untuk digunakan untuk mengimplementasikan solusi yang lebih baik.
a3nm
5
@ a3nm Syscall mungkin pause, tetapi tidak terpapar langsung ke antarmuka shell.
Gilles
PT: Saya tahu screen, tapi ini untuk menjalankan beberapa kejadian program dari skrip shell untuk tujuan pengujian, jadi menggunakan screensedikit berlebihan.
a3nm
3
@sillyMunky Silly Monkey, jawaban WaelJ salah (mengirim nol tanpa batas ke stdin).
PT
48

sleep infinity adalah solusi paling jelas yang saya tahu.

Anda dapat menggunakan infinitykarena sleepmenerima angka floating point * , yang dapat berupa desimal , heksadesimal , tak terhingga , atau NaN , menurut man strtod.

* Ini bukan bagian dari standar POSIX, jadi tidak portabel seperti tail -f /dev/null. Namun, itu didukung dalam GNU coreutils (Linux) dan BSD (digunakan pada Mac) (tampaknya tidak didukung pada versi Mac yang lebih baru - lihat komentar).

Zaz
sumber
Haha, itu pendekatan yang sangat bagus. :)
a3nm
@ a3nm: Terima kasih:) Tampaknya sleep infinityjuga berfungsi pada BSD dan Mac .
Zaz
Sumber daya apa yang diambil oleh proses tidur tanpa batas? Hanya RAM?
CMCDragonkai
1
Jawaban ini mengklaim bahwa sleep infinitymenunggu maksimal 24 hari; siapa yang benar
nh2
1
@Zaz Saya sudah menyelidiki masalah ini secara detail sekarang. Ternyata Anda awalnya benar! The sleeputilitas tidak terbatas pada 24 hari ; itu hanya syscall pertama yang tidur selama 24 hari, dan setelah itu akan melakukan lebih banyak syscalls tersebut. Lihat komentar saya di sini: stackoverflow.com/questions/2935183/…
nh2
19
sleep 2147483647 | program > output &

Ya, 2^31-1adalah angka yang terbatas, dan itu tidak akan berjalan selamanya , tapi saya akan memberi Anda $ 1000 ketika tidur akhirnya habis. (Petunjuk: salah satu dari kita akan mati saat itu.)

  • tidak ada file sementara; memeriksa.
  • tidak ada kesibukan menunggu atau bangun berkala; memeriksa
  • tidak ada utilitas eksotis; memeriksa.
  • sesingkat mungkin. Oke, bisa lebih pendek.
rampok
sumber
5
bash: sleep $ ((64 # 1 _____)) | program> keluaran &
Diego Torres Milano
Itu tidur selama 68 tahun , ini tidur selama 98 abad : sleep 2147483647d...
agc
9

Anda dapat membuat biner yang melakukan hal itu dengan:

$ echo 'int main(){ pause(); }' > pause.c; make pause
PSkocik
sumber
5

Berikut saran lain menggunakan utilitas Unix standar, untuk "tidak melakukan apa-apa, tanpa batas" .

sh -c 'kill -STOP $$' | program > output

Ini akan mengaktifkan sebuah shell yang segera dikirim SIGSTOP, yang menunda proses. Ini digunakan sebagai "input" untuk program Anda. Kelengkapannya SIGSTOPadalah SIGCONT, yaitu jika Anda tahu shell memiliki PID 12345 Anda dapat kill -CONT 12345membuatnya terus.

roaima
sumber
2

Di Linux, Anda dapat melakukan:

read x < /dev/fd/1 | program > output

Di Linux, membuka / dev / fd / x di mana x adalah deskriptor file ke ujung penulisan pipa, membuat Anda ujung pembacaan pipa, jadi di sini sama seperti pada stdin program. Jadi pada dasarnya, readtidak akan pernah kembali, karena satu-satunya hal yang dapat menulis ke pipa itu sendiri, dan readtidak menghasilkan apa pun.

Ini juga akan bekerja pada FreeBSD atau Solaris, tetapi untuk alasan lain. Di sana, membuka / dev / fd / 1 memberi Anda sumber daya yang sama dengan buka pada fd 1 seperti yang Anda harapkan dan seperti kebanyakan sistem kecuali Linux, jadi akhir penulisan pipa. Namun, pada FreeBSD dan Solaris, pipa dua arah. Jadi selama programtidak menulis ke stdin-nya (tidak ada aplikasi tidak), tidak readakan mendapatkan apa pun untuk membaca dari arah pipa.

Pada sistem di mana pipa tidak dua arah, readmungkin akan gagal dengan kesalahan ketika mencoba membaca dari deskriptor file tulis saja. Perhatikan juga bahwa tidak semua sistem memiliki /dev/fd/x.

Stéphane Chazelas
sumber
Sangat bagus! Sebenarnya tes saya, Anda tidak perlu xdengan bash; lebih jauh dengan zsh Anda hanya dapat melakukannya readdan berfungsi (meskipun saya tidak mengerti mengapa!). Apakah trik ini khusus untuk Linux, atau apakah ini berfungsi pada semua sistem * nix?
a3nm
@ A3nm, jika Anda melakukannya readsendiri, itu akan membaca dari stdin. Jadi jika terminal, itu akan membaca apa yang Anda ketik sampai Anda menekan enter.
Stéphane Chazelas
tentu, saya mengerti apa yang dibaca tidak. Apa yang saya tidak mengerti adalah mengapa membaca dari terminal dengan membaca dalam proses latar belakang diblokir dengan bash tetapi tidak dengan zsh.
a3nm
@ A3nm, saya tidak yakin apa yang Anda maksud. Apa yang Anda maksud dengan yang bisa Anda lakukan readdan berhasil ?
Stéphane Chazelas
Saya mengatakan bahwa dengan zsh Anda hanya dapat melakukan read | program > outputdan bekerja dengan cara yang sama seperti yang Anda sarankan. (Dan saya tidak mengerti mengapa.)
a3nm
0

Stéphane Chazelas' read solusi bekerja pada Mac OS X serta jika fd membaca akan dibuka pada /dev/fd/1.

# using bash on Mac OS X
# -bash: /dev/fd/1: Permission denied
read x </dev/fd/1 | cat >/dev/null
echo ${PIPESTATUS[*]}   #  1 0

exec 3<&- 3</dev/fd/1
read x 0<&3 | cat >/dev/null
echo ${PIPESTATUS[*]}   #  0 0

Untuk dapat membunuh tail -f /dev/nulldalam skrip (dengan SIGINT, misalnya) perlu untuk latar belakang tailperintah dan wait.

#!/bin/bash
# ctrl-c will kill tail and exit script
trap 'trap - INT; kill "$!"; exit' INT
exec tail -f /dev/null & wait $!
user6915
sumber
-2

Redirect /dev/zerosebagai input standar!

program < /dev/zero > output &
Astaga
sumber
9
Ini akan memberikan programnya jumlah nol-byte yang tak terbatas ... yang, sayangnya, akan membuatnya sibuk.
Jander
1
Ini bukan benar-benar jander, / dev / zero tidak akan pernah menutup, memegang rantai pipa terbuka. Namun, poster mengatakan dia tidak menerima stdin, jadi tidak ada nol yang akan ditransfer ke program. Ini bukan loop yang sibuk sama sekali, ini adalah menunggu murni.
sillyMunky
2
maaf, OP memang menggunakan stdin, jadi ini akan menghapus inputnya dan akan menarik dari / dev / nol. Saya harus membaca dua kali lain kali! Jika OP tidak menggunakan stdin, ini akan menjadi solusi paling elegan yang pernah saya lihat, dan tidak akan menjadi penantian yang sibuk.
sillyMunky