web-dev-qa-db-fra.com

Comment ne rien faire pour toujours d'une manière élégante?

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

87
a3nm

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

  • pas de processus supplémentaire
  • vous pouvez quitter le script lorsque program meurt (utilisez wait pour l'attendre)
  • program se terminera (récupérez eof sur son stdin si le shell se ferme).
17
Stéphane Chazelas

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?

81
P.T.

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

50
Zaz
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à.)

  • pas de fichiers temporaires; vérifier.
  • pas d'attente prolongée ou de réveils périodiques; vérifier
  • pas d'utilitaires exotiques; vérifier.
  • aussi court que possible. D'accord, cela pourrait être plus court.
19
Rob

Vous pouvez créer un binaire qui fait exactement cela avec:

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

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.

5
roaima

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.

2
Stéphane Chazelas

Stéphane Chazelas 'readsolution 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 $!
0
user6915