Como não fazer nada para sempre de uma maneira elegante?

72

Eu tenho um programa que produz informações úteis sobre stdout , mas também leituras de stdin . Eu quero redirecionar sua saída padrão para um arquivo sem fornecer nada na entrada padrão. Até aí, tudo bem: eu posso fazer:

program > output

e não faça nada no tty.

No entanto, o problema é que eu quero fazer isso em segundo plano. Se eu fizer:

program > output &

o programa será suspenso ("suspenso (tty input)").

Se eu fizer:

program < /dev/null > output &

o programa termina imediatamente porque atinge o EOF.

Parece que o que preciso é canalizar para program algo que não faz nada por um período indefinido de tempo e não lê stdin . As seguintes abordagens funcionam:

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

No entanto, isso tudo é muito feio. Há para ser uma maneira elegante, usando utilitários padrão do Unix, para "não fazer nada, indefinidamente" (parafraseando man true ). Como eu poderia conseguir isso? (Meus critérios principais para elegância aqui: nenhum arquivo temporário; nenhum despertar ocupado ou espera periódica; nenhum utilitário exótico; tão curto quanto possível.)

    
por a3nm 12.07.2012 / 20:42

9 respostas

13

Em shells que os suportam (ksh, zsh, bash4), você pode iniciar program como um co-processo .

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

Isso inicia program em segundo plano com sua entrada redirecionada de pipe . A outra extremidade do tubo está aberta para o shell.

Três benefícios dessa abordagem

  • sem processo extra
  • você pode sair do script quando program morre (use wait para esperar)
  • program terminará (obtenha eof em seu stdin se o shell sair).
por 12.08.2015 / 09:24
69

Eu não acho que você vai ficar mais elegante do que o

tail -f /dev/null

que você já sugeriu (supondo que isso use inotify internamente, não deve haver polling ou wakeups, então além de parecer estranho, deve ser suficiente).

Você precisa de um utilitário que será executado indefinidamente, manterá seu stdout aberto, mas, na verdade, não gravará nada no stdout e não sairá quando seu stdin for fechado. Algo como yes realmente grava no stdout. cat sairá quando seu stdin for fechado (ou o que você redirecionar para ele). Acho que sleep 1000000000d pode funcionar, mas o tail é claramente melhor. Minha caixa Debian tem um tailf que encurta um pouco o comando.

Com uma abordagem diferente, que tal executar o programa em screen ?

    
por 12.07.2012 / 21:33
41

sleep infinity é a solução mais clara que eu conheço.

Você pode usar infinity porque sleep aceita um número de ponto flutuante * , que pode ser decimal , hexadecimal , infinito ou NaN , de acordo com man strtod .

* Isso não faz parte do padrão POSIX, então não é tão portátil quanto tail -f /dev/null . No entanto, ele é suportado no GNU coreutils (Linux) e no BSD (usado no Mac) (aparentemente não suportado em versões mais recentes do Mac - veja os comentários).

    
por 07.05.2014 / 12:05
16
sleep 2147483647 | program > output &

Sim, 2^31-1 é um número finito e não será executado para sempre , mas darei $ 1000 quando o tempo de espera expirar. (Dica: um de nós estará morto até lá.)

  • sem arquivos temporários; verifique.
  • não há esperas de ocupado ou periódicas; verifique
  • sem utilitários exóticos; verifique.
  • o mais curto possível. Ok, poderia ser mais curto.
por 12.07.2012 / 23:47
7

Você pode criar um binário que faça exatamente isso com:

$ echo 'int main(){ pause(); }' > pause.c; make pause
    
por 11.08.2015 / 21:07
5

Aqui está outra sugestão usando utilitários padrão do Unix, para "não fazer nada, indefinidamente" .

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

Isso dispara um shell que é enviado imediatamente SIGSTOP , o que suspende o processo. Isso é usado como "entrada" para o seu programa. O complemento de SIGSTOP é SIGCONT , ou seja, se você sabe que o shell tem o PID 12345, você pode kill -CONT 12345 para continuar.

    
por 11.08.2015 / 22:13
2

No Linux, você pode fazer:

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

No Linux, abrir / dev / fd / x onde x é um descritor de arquivo para a extremidade de gravação de um pipe, você obtém o final de leitura do pipe, portanto, aqui é o mesmo que no stdin do programa. Então, basicamente, read nunca retornará, porque a única coisa que pode gravar nesse pipe é ela mesma, e read não produz nada.

Ele também funciona no FreeBSD ou no Solaris, mas por outro motivo. Lá, a abertura de / dev / fd / 1 fornece o mesmo recurso que o fd1 aberto como seria de esperar, e como a maioria dos sistemas, exceto o Linux, faz, portanto, o fim da escrita do pipe. No entanto, no FreeBSD e no Solaris, os pipes são bidirecionais. Portanto, contanto que program não grave em seu stdin (nenhum aplicativo o faz), read não obterá nada para ler a partir daquela direção do pipe.

Em sistemas nos quais os pipes não são bidirecionais, read provavelmente falhará com um erro ao tentar ler de um descritor de arquivo somente de gravação. Observe também que nem todos os sistemas têm /dev/fd/x .

    
por 11.08.2015 / 22:53
0

Stéphane Chazelas ' read solução também funciona no Mac OS X se um fd de leitura for aberto em /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/null em um script (com SIGINT, por exemplo), é necessário fazer o background do comando tail e wait .

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

Redirecionar /dev/zero como entrada padrão!

program < /dev/zero > output &
    
por 13.07.2012 / 01:29