¿Cómo no hacer nada para siempre de una manera elegante?

82

Tengo un programa que produce información útil stdoutpero también lee stdin. Quiero redirigir su salida estándar a un archivo sin proporcionar nada en la entrada estándar. Hasta ahora, todo bien: puedo hacer:

program > output

y no hagas nada en el tty.

Sin embargo, el problema es que quiero hacer esto en segundo plano. Si lo hago:

program > output &

el programa se suspenderá ("suspendido (entrada tty)").

Si lo hago:

program < /dev/null > output &

el programa termina inmediatamente porque llega a EOF.

Parece que lo que necesito es conectarme a programalgo que no hace nada por un tiempo indefinido y que no lee stdin. Los siguientes enfoques funcionan:

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

Sin embargo, todo esto es muy feo. No tiene que ser una manera elegante, con las utilidades estándar de Unix, "no hacer nada, por tiempo indefinido" (parafraseando man true). ¿Cómo podría lograr esto? (Mi criterio principal para la elegancia aquí: sin archivos temporales; sin esperas ocupadas o despertares periódicos; sin utilidades exóticas; lo más corto posible).

a3nm
fuente
Tratar su -c 'program | output &' user. Estoy a punto de hacer una pregunta similar con la creación de trabajos en segundo plano como un método aceptable para manejar un "servicio / demonio". También noté que no podía redirigir STDERRsin también redirigir STDOUT. La solución en la que programą envía STDOUTa STDINde programB, a continuación, vuelve a dirigir STDERRa un archivo de registro:programA 2> /var/log/programA.log | programB 2> /var/log/programB.log 1> /dev/null
mbrownnyc
tal vez ... su -c 'while true; do true; done | cat > ~/output &' user?
mbrownnyc
¿Qué tipo de programa es ese?
João Portela
João Portela: Este es un programa que escribí, gitorious.org/irctk
a3nm
¿Por qué no simplemente agregar un interruptor a ese programa que escribió? Además, supongo que si cierra stdin con 1<&-él, ¿saldrá de su programa?
w00t

Respuestas:

16

En los shells que los admiten (ksh, zsh, bash4), puede comenzar programcomo un coproceso .

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

Eso comienza programen segundo plano con su entrada redirigida desde a pipe. El otro extremo de la tubería está abierto a la carcasa.

Tres beneficios de ese enfoque

  • sin proceso extra
  • puedes salir del script cuando programmuera (úsalo waitpara esperarlo)
  • programterminará (obtenga eofsu stdin si el shell sale).
Stéphane Chazelas
fuente
¡Parece que funciona y parece una gran idea! (Para ser justos, había pedido algo para canalizar a mi comando, no para una función de shell, pero este era solo el problema XY en juego). Estoy considerando aceptar esta respuesta en lugar de la respuesta de @ PT.
a3nm
1
@ a3nm, tail -f /dev/nullno es ideal ya que se lee cada segundo /dev/null(las versiones actuales de GNU tail en Linux usando inotify en realidad son un error ). sleep info su equivalente más portátil sleep 2147483647son mejores enfoques para un comando que se encuentra allí sin hacer nada IMO (tenga en cuenta que sleepestá construido en unos pocos shells como ksh93o mksh).
Stéphane Chazelas
78

No creo que te vayas a poner más elegante que el

tail -f /dev/null

que ya sugirió (suponiendo que este uso inotifique internamente, no debería haber encuestas ni reactivaciones, por lo que aparte de tener un aspecto extraño, debería ser suficiente).

Necesita una utilidad que se ejecutará indefinidamente, mantendrá su stdout abierto, pero en realidad no escribirá nada en stdout, y no saldrá cuando se cierre su stdin. Algo así yesrealmente escribe en stdout. catsaldrá cuando su stdin esté cerrado (o lo que sea que redireccione a él está hecho). Creo que sleep 1000000000dpodría funcionar, pero tailclaramente es mejor. Mi cuadro de Debian tiene un tailfcomando que acorta un poco.

Tomando un rumbo diferente, ¿qué tal si ejecutamos el programa screen?

PT
fuente
Me gusta más el tail -f /dev/nullenfoque y también lo encuentro lo suficientemente elegante, ya que el uso del comando coincide bastante con el propósito previsto.
jw013
2
Por strace tail -f /dev/nulllo que parece, los tailusos inotifyy los despertares ocurren en casos tontos sudo touch /dev/null. Es triste que parezca que no hay una mejor solución ... Me pregunto cuál sería la llamada al sistema correcta para implementar una mejor solución.
a3nm
55
@ a3nm La llamada al sistema sería pause, pero no está expuesta directamente a una interfaz de shell.
Gilles
PT: Lo sé screen, pero esto es para ejecutar múltiples apariciones del programa desde un script de shell con fines de prueba, por lo que usarlo screenes un poco exagerado.
a3nm
3
@sillyMunky Silly Monkey, la respuesta de WaelJ es incorrecta (envía ceros infinitos a stdin).
PT
48

sleep infinity es la solución más clara que conozco.

Puede usar infinityporque sleepacepta un número de coma flotante * , que puede ser decimal , hexadecimal , infinito o NaN , de acuerdo con man strtod.

* Esto no es parte del estándar POSIX, por lo que no es tan portátil como tail -f /dev/null. Sin embargo, es compatible con GNU coreutils (Linux) y BSD (utilizado en Mac) (aparentemente no es compatible con las versiones más nuevas de Mac - ver comentarios).

Zaz
fuente
Jaja, ese es realmente un buen enfoque. :)
a3nm
@ a3nm: Gracias :) Parece que sleep infinitytambién funciona en BSD y Mac .
Zaz
¿Qué tipo de recursos requiere un proceso de sueño infinito? ¿Solo RAM?
CMCDragonkai
1
Esta respuesta afirma que sleep infinityespera 24 días como máximo; quien tiene razon
nh2
1
@Zaz He investigado el problema en detalle ahora. ¡Resulta que inicialmente estabas en lo correcto! La sleeputilidad no está limitada a 24 días ; es solo el primer syscall que duerme durante 24 días, y luego hará más syscalls. Vea mi comentario aquí: stackoverflow.com/questions/2935183/…
nh2
19
sleep 2147483647 | program > output &

Sí, 2^31-1es un número finito, y no se ejecutará para siempre , pero te daré $ 1000 cuando el sueño finalmente se agote. (Sugerencia: uno de nosotros estará muerto para entonces).

  • sin archivos temporales; cheque.
  • sin esperas ocupadas o despertares periódicos; cheque
  • sin utilidades exóticas; cheque.
  • tan corto como sea posible. De acuerdo, podría ser más corto.
Robar
fuente
55
bash: dormir $ ((64 # 1 _____)) | programa> salida y
Diego Torres Milano
Que duerme durante 68 años , duerme durante 98 siglos : sleep 2147483647d...
agc
9

Puede crear un binario que haga exactamente eso con:

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

Aquí hay otra sugerencia usando las utilidades estándar de Unix, para "no hacer nada, indefinidamente" .

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

Esto activa un shell que se envía inmediatamente SIGSTOP, lo que suspende el proceso. Esto se utiliza como "entrada" a su programa. El complemento de SIGSTOPes SIGCONT, es decir, si sabe que el shell tiene PID 12345, puede kill -CONT 12345hacerlo continuar.

roaima
fuente
2

En Linux, puedes hacer:

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

En Linux, abrir / dev / fd / x donde x es un descriptor de archivo al final de la escritura de una tubería, le da el final de la lectura de la tubería, así que aquí es lo mismo que en el stdin del programa. Entonces, básicamente, readnunca volverá, porque lo único que puede escribir en esa tubería es en sí mismo, y readno genera nada.

También funcionará en FreeBSD o Solaris, pero por otra razón. Allí, abrir / dev / fd / 1 le da el mismo recurso que abrir en fd 1 como lo esperaría y como la mayoría de los sistemas, excepto Linux, lo hacen al final de la escritura. Sin embargo, en FreeBSD y Solaris, las tuberías son bidireccionales. Por lo tanto, siempre programque no escriba en su stdin (ninguna aplicación lo hace), readno obtendrá nada que leer desde esa dirección de la tubería.

En sistemas donde las tuberías no son bidireccionales, reades probable que falle con un error al intentar leer desde un descriptor de archivo de solo escritura. También tenga en cuenta que no todos los sistemas tienen /dev/fd/x.

Stéphane Chazelas
fuente
¡Muy agradable! De hecho, mis pruebas no necesitas el xcon bash; Además, con zsh puedes hacer ready funciona (¡aunque no entiendo por qué!). ¿Es este truco específico de Linux, o funciona en todos los sistemas * nix?
a3nm
@ a3nm, si lo haces readsolo, se leerá desde stdin. Entonces, si es la terminal, leerá lo que escribe hasta que presione enter.
Stéphane Chazelas
Claro, entiendo lo que hace la lectura. Lo que no entiendo es por qué leer desde la terminal con lectura en un proceso en segundo plano está bloqueando con bash pero no con zsh.
a3nm
@ a3nm, no estoy seguro de lo que quieres decir. ¿Qué quieres decir con que simplemente puedes hacer ready funciona ?
Stéphane Chazelas
Estoy diciendo que con zsh puedes hacer read | program > outputy funciona de la misma manera que lo sugerido. (Y no entiendo por qué.)
a3nm
0

La read solución de Stéphane Chazelas también funciona en Mac OS X si se abre un fd de lectura /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

Para poder matar tail -f /dev/nullen un script (con SIGINT, por ejemplo) es necesario poner en segundo plano el tailcomando y wait.

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

Redireccionar /dev/zerocomo entrada estándar!

program < /dev/zero > output &
WaelJ
fuente
99
Esto le daría a su programa un número infinito de bytes cero ... que, lamentablemente, lo haría ocupado.
Jander
1
Esto no es cierto jander, / dev / zero nunca se cerrará, manteniendo abierta la cadena de tuberías. Sin embargo, el póster dice que no toma stdin, por lo que no se transferirán ceros al programa. Este no es un bucle ocupado, es una pura espera.
sillyMunky
2
lo siento, OP usa stdin, por lo que esto borrará su entrada y se extraerá de / dev / zero. ¡Debería leer dos veces la próxima vez! Si OP no estaba usando stdin, esta sería la solución más elegante que he visto, y no sería una espera ocupada.
sillyMunky