J'ai un programme qui produit des informations utiles sur stdout
mais lit également à partir de stdin
. Je veux rediriger sa sortie standard vers un fichier sans rien fournir sur l'entrée standard. Jusqu'ici, tout va bien: je peux faire:
program > output
et ne faites rien dans le tty.
Cependant, le problème est que je veux le faire en arrière-plan. Si je fais:
program > output &
le programme sera suspendu ("suspendu (entrée tty)").
Si je fais:
program < /dev/null > output &
le programme se termine immédiatement car il atteint EOF.
Il semble que ce dont j'ai besoin soit de diriger dans program
quelque chose qui ne fait rien pendant une durée indéterminée et ne lit pas stdin
. Les approches suivantes fonctionnent:
while true; do sleep 100; done | program > output &
mkfifo fifo && cat fifo | program > output &
tail -f /dev/null | program > output &
Cependant, tout cela est très moche. Il a pour être un moyen élégant, en utilisant les utilitaires Unix standard, de "ne rien faire, indéfiniment" (pour paraphraser man true
). Comment pourrais-je y parvenir? (Mes principaux critères d'élégance ici: pas de fichiers temporaires; pas d'attente en attente ou de réveils périodiques; pas d'utilitaires exotiques; aussi court que possible.)
Dans les shells qui les supportent (ksh, zsh, bash4), vous pouvez démarrer program
comme co-process .
ksh
: program > output |&
zsh
, bash
: coproc program > output
Cela démarre program
en arrière-plan avec son entrée redirigée à partir d'un pipe
. L'autre extrémité du tuyau est ouverte sur le Shell.
Trois avantages de cette approche
program
meurt (utilisez wait
pour l'attendre)program
se terminera (récupérez eof
sur son stdin si le shell se ferme).Je ne pense pas que vous allez devenir plus élégant que le
tail -f /dev/null
que vous avez déjà suggéré (en supposant que cela utilise inotify en interne, il ne devrait pas y avoir d'interrogation ou de réveil, donc en plus d'être étrange, cela devrait être suffisant).
Vous avez besoin d'un utilitaire qui fonctionnera indéfiniment, gardera sa stdout ouverte, mais n'écrira rien sur stdout et ne se fermera pas lorsque son stdin est fermé. Quelque chose comme yes
écrit en fait sur stdout. cat
se fermera lorsque son stdin est fermé (ou tout ce que vous redirigez dedans est fait). Je pense sleep 1000000000d
pourrait fonctionner, mais le tail
est clairement meilleur. Ma boîte Debian a un tailf
qui raccourcit légèrement la commande.
En adoptant une approche différente, que diriez-vous d'exécuter le programme sous screen
?
sleep infinity
est la solution la plus claire que je connaisse.
Vous pouvez utiliser infinity
car sleep
accepte un nombre à virgule flottante *, qui peut être décimal, hexadécimal , infini, ou NaN, selon man strtod
.
* Cela ne fait pas partie de la norme POSIX, il n'est donc pas aussi portable que tail -f /dev/null
. Cependant, il est pris en charge dans GNU coreutils (Linux) et BSD (utilisé sur Mac) (apparemment non pris en charge sur les nouvelles versions de Mac - voir les commentaires).
sleep 2147483647 | program > output &
Oui, 2^31-1
est un nombre fini, et il ne s'exécutera pas pour toujours, mais je vous donnerai 1000 $ lorsque le sommeil arrivera à expiration. (Indice: l'un d'entre nous sera mort d'ici là.)
Vous pouvez créer un binaire qui fait exactement cela avec:
$ echo 'int main(){ pause(); }' > pause.c; make pause
Voici une autre suggestion en utilisant les utilitaires Unix standard, pour "ne rien faire, indéfiniment".
sh -c 'kill -STOP $$' | program > output
Cela déclenche un Shell qui est immédiatement envoyé SIGSTOP
, ce qui suspend le processus. Ceci est utilisé comme "entrée" pour votre programme. Le complément de SIGSTOP
est SIGCONT
, c'est-à-dire que si vous savez que le Shell a le PID 12345, vous pouvez kill -CONT 12345
pour continuer.
Sous Linux, vous pouvez faire:
read x < /dev/fd/1 | program > output
Sous Linux, ouvrir/dev/fd/x où x est un descripteur de fichier à la fin de l'écriture d'un tuyau, vous donne la fin de lecture du tuyau, alors voici la même chose que sur le stdin du programme. Donc, fondamentalement, read
ne reviendra jamais, car la seule chose qui peut écrire dans ce canal est elle-même, et read
ne produit rien.
Il fonctionnera également sur FreeBSD ou Solaris, mais pour une autre raison. Là, l'ouverture de/dev/fd/1 vous donne la même ressource que open sur fd 1 que vous attendez et comme la plupart des systèmes sauf Linux, donc la fin d'écriture du tube. Cependant, sur FreeBSD et Solaris, les canaux sont bidirectionnels. Donc tant que program
n'écrit pas dans son stdin (aucune application ne le fait), read
n'aura rien à lire dans cette direction du tube.
Sur les systèmes où les canaux ne sont pas bidirectionnels, read
échouera probablement avec une erreur lors de la tentative de lecture à partir d'un descripteur de fichier en écriture seule. Notez également que tous les systèmes n'ont pas /dev/fd/x
.
Stéphane Chazelas 'read
solution fonctionne aussi sur Mac OS X si un fd de lecture est ouvert sur /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
Pour pouvoir tuer tail -f /dev/null
dans un script (avec SIGINT, par exemple) il est nécessaire d'arrière-plan la commande tail
et wait
.
#!/bin/bash
# ctrl-c will kill tail and exit script
trap 'trap - INT; kill "$!"; exit' INT
exec tail -f /dev/null & wait $!