Sonsuza dek zarif bir şekilde hiçbir şey yapmadan?


82

Faydalı bilgiler üreten stdoutama aynı zamanda okuyan bir program var stdin. Standart çıktıda hiçbir şey sağlamadan standart çıktısını bir dosyaya yönlendirmek istiyorum. Şimdiye kadar, çok iyi: Yapabilirim:

program > output

ve tty içinde hiçbir şey yapmayın.

Ancak sorun şu ki bunu arka planda yapmak istiyorum. Eğer yaparsam:

program > output &

program askıya alınacaktır ("askıya alınmış (tty girişi)").

Eğer yaparsam:

program < /dev/null > output &

Program EOF'a ulaştığı için derhal sona erer.

İhtiyacım programolan şey, belirsiz bir süre boyunca hiçbir şey yapmayan ve okumayan bir şeye bağlanmak stdin. Aşağıdaki yaklaşımlar işe yarar:

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

Ancak, bu hepsi çok çirkin. Orada sahiptir (tefsir "süresiz, hiçbir şey yapmak" için, standart Unix araçları kullanılarak, zarif bir yolu olmalı man true). Bunu nasıl başarabilirim? (Buradaki zarafet için temel kriterlerim: geçici dosyalar yok, meşgul beklemede veya periyodik uyandırmalar; egzotik araçlar yok; mümkün olduğunca kısa.)


Dene su -c 'program | output &' user. Benzer bir soruyu, bir "hizmet / arka plan programı" işlemek için kabul edilebilir bir yöntem olarak arka plan işleri oluşturma konusunda sormak üzereyim. Ayrıca, yönlendirmeden STDERRde yönlendirme yapamadığımı fark ettim STDOUT. Programa gönderir çözüm STDOUTiçin STDINprogramB birleştirdikten sonra yönlendirir STDERRbir günlük dosyasına:programA 2> /var/log/programA.log | programB 2> /var/log/programB.log 1> /dev/null
mbrownnyc

belki ... su -c 'while true; do true; done | cat > ~/output &' user?
mbrownnyc

Bu ne tür bir program?
João Portela,

João Portela: Bu yazdığım bir program, gitorious.org/irctk
a3nm

Neden yazdığın programa bir geçiş yapmıyorsun? Ayrıca, stdin ile kapatırsanız 1<&-programdan çıkacağını varsayıyorum ?
w00t

Yanıtlar:


16

Onları destekleyen mermilerde (ksh, zsh, bash4) programbir ortak işlem olarak başlayabilirsiniz .

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

Bu program, a'dan yönlendirilen girişi ile arka planda başlar pipe. Borunun diğer ucu kabuğa açıktır.

Bu yaklaşımın üç faydası

  • fazladan işlem yok
  • programöldüğünde komut dosyasından çıkabilirsiniz ( waitbeklemek için kullanın )
  • programsonlanacaktır ( eofkabuk çıkarsa stdin'i alın).

Bu iş gibi görünüyor ve harika bir fikir gibi görünüyor! (Adil olmak gerekirse, bir kabuk özelliği için emir almam için bir şey sormuştum, ama bu sadece oyundaki XY problemiydi.) @ PT'nin yerine bu cevabı kabul etmeyi düşünüyorum.
a3nm

1
@ a3nm, tail -f /dev/nullher saniyede bir okuma yaptığı için ideal değildir /dev/null(Linux'taki GNU kuyruğunun şu anki sürümleri inotify kullanarak aslında bir hata vardır ). sleep infveya daha fazla taşınabilir eşdeğeri sleep 2147483647, IMO hiçbir şey yapmadan oraya oturan bir komut için daha iyi yaklaşımlardır ( veya sleepgibi birkaç kabuğa inşa edildiğine dikkat edin ). ksh93mksh
Stéphane Chazelas

78

Bundan daha şık olacağınızı sanmıyorum.

tail -f /dev/null

Zaten önerdiğiniz (bu kullanımların dahili olarak inotifiye edildiği varsayımıyla, yoklama veya uyandırma olmamalıdır, bu yüzden tuhaf görünmekten başka, yeterli olması gerekir).

Süresiz çalışacak, stdout'unu açık tutacak, gerçekte stdout'a hiçbir şey yazmayacak ve std kapalı olduğunda çıkmayacak bir programa ihtiyacınız var. Gibi bir şey yesaslında stdout için yazıyor. catstdin kapalı olduğunda çıkacaktır (veya ne yönlendirirseniz yapın). Bence sleep 1000000000dişe yarayabilir, ama tailaçıkça daha iyi. Debian tailfkutumda bu, komutu biraz kısaltır.

Farklı bir iş takip ederek, programı yürütmeye ne dersiniz screen?


tail -f /dev/nullKomut kullanımı, amaçlanan amaç ile oldukça yakından uyuştuğundan , yaklaşımı en iyi seviyorum ve yeterince şık buluyorum.
jw013

2
Gönderen strace tail -f /dev/nullöyle görünüyor tailkullanır inotifywakeups gibi saçma durumlarda ortaya çıkacağına ve sudo touch /dev/null. Daha iyi bir çözüm yok gibi görünüyor ... Acaba hangisinin daha iyi bir çözüm uygulamak için kullanacağı doğru çağrı olacağını merak ediyorum.
a3nm

5
@ a3nm Sistem çağrısı olur pause, ancak doğrudan bir kabuk arayüzüne maruz kalmaz.
Gilles,

PT: Biliyorum screen, ama bu testin bir kabuk betiğinden programın çoklu oluşumlarını test amaçlı çalıştırmaktır, bu yüzden screenkullanımı biraz fazladan önemlidir.
a3nm

3
@sillyMunky Silly Monkey, WaelJ'in cevabı yanlış (stdin'e sonsuz sıfırlar gönderir).
PT

48

sleep infinity bildiğim en net çözüm.

Sen kullanabilirsiniz infinityçünkü sleepbir kayan nokta sayı kabul eder * olabilir, ondalık , onaltılık , sonsuzluk veya NaN göre man strtod.

* Bu POSIX standardının bir parçası değildir, o kadar taşınabilir değildir tail -f /dev/null. Ancak, GNU coreutils (Linux) ve BSD'de (Mac'te kullanılır) desteklenir (görünüşe göre daha yeni Mac sürümlerinde desteklenmez - yorumları görün).


Haha, bu gerçekten hoş bir yaklaşım. :)
a3nm

@ a3nm: Teşekkürler:) Görünüşe göre BSD ve Mac üzerindesleep infinity de çalışıyor .
Zaz

Sonsuz bir uyku süreci nasıl bir kaynak gerektirir? Sadece RAM?
CMCDragonkai

1
Bu cevapsleep infinity en fazla 24 gün beklediğini iddia ediyor ; kim haklı
nh2

1
@ Zaz Konuyu şimdi detaylı olarak araştırdım. İlk başta doğru olduğun ortaya çıktı! Hizmet sleepprogramı 24 gün ile sınırlı değildir ; bu sadece bir ilk 24 gün boyunca uyur syscall ve sonrasında daha böyle syscalls yapacağız. Buraya
yorumuma

19
sleep 2147483647 | program > output &

Evet, 2^31-1sonlu bir sayı ve sonsuza dek sürmeyecek , ama uyku en sonunda zaman aşımına uğradığında 1000 $ vereceğim. (İpucu: Birimiz o zamana kadar ölmüş olacak.)

  • geçici dosya yok; Kontrol.
  • Meşgul bekleyen ya da periyodik uyanmalar yok; Kontrol
  • egzotik hizmet yok; Kontrol.
  • mümkün olduğu kadar kısa. Tamam, daha kısa olabilir.

5
bash: uyu $ ((64 # 1 _____)) | program> çıkış &
Diego Torres Milano

Bu 68 yıl boyunca uyuyor, bu 98 yüzyıl boyunca uyuyor : sleep 2147483647d...
agc

9

Sadece bunu yapan bir ikili dosya oluşturabilirsiniz:

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

5

Burada başka bir öneri "süresiz, hiçbir şey yapmak" için, standart Unix araçları kullanılarak .

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

Bu hemen gönderilen SIGSTOPve işlemi askıya alan bir kabuk ateşler . Bu, programınıza "giriş" olarak kullanılır. Tamamlayıcısı SIGSTOPolan SIGCONTkabuk yapabilirsiniz PID 12345 sahiptir biliyorsanız, yani kill -CONT 12345o devam yapmak.


2

Linux'ta şunları yapabilirsiniz:

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

Linux'ta, / dev / fd / x 'in açılması, burada x, bir borunun yazım ucuna bir dosya tanıtıcısıdır, borunun okuma ucunu alır, burada programın stdin'inde olduğu gibi. Dolayısıyla, temelde, readasla geri dönmeyecek, çünkü o boruya yazabilecek tek şey kendisidir ve readhiçbir şey üretmez.

Ayrıca FreeBSD veya Solaris'te de çalışacak, ancak başka bir nedenle. Burada, / dev / fd / 1 'i açmak beklediğiniz gibi fd 1' de açıldığı gibi aynı kaynağı ve Linux dışındaki çoğu sistemin yaptığı gibi, borunun yazımının sona ermesini sağlar. Bununla birlikte, FreeBSD ve Solaris'te borular çift yönlüdür. programStdin'e yazmadığı sürece (hiçbir uygulama yapmaz), readborunun bu yönünden okumak için hiçbir şey almayacaktır.

Boruların çift yönlü olmadığı sistemlerde, readsalt okunur bir dosya tanımlayıcısından okumaya çalışırken büyük olasılıkla bir hata ile başarısız olur. Ayrıca tüm sistemlerin olmadığını unutmayın /dev/fd/x.


Çok hoş! Aslında benim testlerime xbash ile ihtiyacınız yok ; zsh ile daha da fazlasını yapabilirsiniz readve işe yarar (nedenini anlamamıza rağmen!). Bu numara Linux'a özgü mü yoksa tüm * nix sistemlerinde mi çalışıyor?
a3nm

@ a3nm, readyalnız yaparsanız , stdin'den okuyacak. Bu yüzden terminalse, siz girinceye kadar yazdıklarınızı okuyacaktır.
Stéphane Chazelas

Elbette, okudukların ne yaptığını anlıyorum. Anlamadığım şey, arka plandaki bir süreçte okunan terminalden okunmasının neden bash ile bloke ettiği, ancak zsh ile engellemediğidir.
a3nm

@ a3nm, ne demek istediğinden emin değilim. Ne demek sadece yapabilir readve işe yarıyor ?
Stéphane Chazelas

Zsh ile yapabileceğini read | program > outputve önerdiğin gibi çalıştığını söylüyorum. (Ve nedenini anlamıyorum.)
a3nm

0

Stéphane Chazelas'ın read çözümü Mac OS X'te de çalışır ve eğer bir okuma fd'si açılırsa /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

tail -f /dev/nullBir komut dosyasında öldürmek için (örneğin, SIGINT ile) tailkomutun arkaplanı ve arkaplanı gereklidir wait.

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

-2

/dev/zeroStandart giriş olarak yönlendir !

program < /dev/zero > output &

9
Bu, programına sonsuz sayıda sıfır bayt verirdi ... ki bu ne yazık ki meşgul döngü yapardı.
Jander

1
Bu doğru jander değil, / dev / zero asla boru zincirini açık tutarak asla kapanmayacak. Bununla birlikte, poster onun stdin'i almadığını, dolayısıyla hiçbir zaman programa aktarılmadığını söylüyor. Bu hiç meşgul bir döngü değil, saf bir bekleyiş.
sillyMunky

2
üzgünüm, OP stdin kullanıyor, bu onun girişini silecek ve / dev / zero'dan çizim yapacak. Bir dahaki sefere iki kez okumalıyım! OP stdin kullanmıyorsa, bu gördüğüm en şık çözüm olurdu ve yoğun bir bekleyiş olmazdı.
sillyMunky
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.