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

82

Eu tenho um programa que produz informações úteis, stdoutmas também lê stdin. Quero redirecionar sua saída padrão para um arquivo sem fornecer nada na entrada padrão. Até agora, 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 ("suspendido (entrada tty)").

Se eu fizer:

program < /dev/null > output &

o programa termina imediatamente porque alcança EOF.

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

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

No entanto, tudo isso é muito feio. Não tem que ser uma maneira elegante, usando utilitários Unix padrão, para "não fazer nada, por tempo indeterminado" (parafraseando man true). Como eu consegui isso? (Meus principais critérios de elegância aqui: sem arquivos temporários; sem espera ocupada ou ativações periódicas; sem utilitários exóticos; o mais curto possível).

a3nm
fonte
Tente su -c 'program | output &' user. Estou prestes a fazer uma pergunta semelhante com a criação de trabalhos em segundo plano como um método aceitável para lidar com um "serviço / daemon". Também notei que não era possível redirecionar STDERRsem também redirecionar STDOUT. A solução onde o Programa envia STDOUTa STDINde programB, então redireciona STDERRpara um arquivo de log:programA 2> /var/log/programA.log | programB 2> /var/log/programB.log 1> /dev/null
mbrownnyc
talvez ... su -c 'while true; do true; done | cat > ~/output &' user?
mbrownnyc
que tipo de programa é esse?
João Portela
João Portela: Este é um programa que eu escrevi, gitorious.org/irctk
a3nm
Por que não simplesmente adicionar uma opção ao programa que você escreveu? Além disso, suponho que se você fechar o stdin com 1<&-ele sairá do seu programa?
w00t

Respostas:

16

Nos shells que os suportam (ksh, zsh, bash4), você pode iniciar programcomo um co-processo .

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

Isso começa programem segundo plano com sua entrada redirecionada de a pipe. A outra extremidade do tubo está aberta para a concha.

Três benefícios dessa abordagem

  • nenhum processo extra
  • você pode sair do script quando programmorrer (use waitpara esperar)
  • programterminará (entre eofem seu stdin se o shell sair).
Stéphane Chazelas
fonte
Isso parece funcionar e parece uma ótima ideia! (Para ser justo, eu pedi algo para canalizar ao meu comando, não um recurso de shell, mas esse era apenas o problema XY em jogo.) Estou considerando aceitar essa resposta em vez da resposta de @ PT.
A3nm
1
@ a3nm, tail -f /dev/nullnão é o ideal, pois ele faz uma leitura a cada segundo /dev/null(as versões atuais do GNU tail no Linux usando inotify há realmente um bug ). sleep infou seu equivalente mais portátil, sleep 2147483647são abordagens melhores para um comando que fica lá sem fazer nada IMO (observe que ele sleepé construído em alguns shells como ksh93ou mksh).
Stéphane Chazelas
78

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

tail -f /dev/null

que você já sugeriu (supondo que esse uso seja inotificado internamente, não deve haver pesquisas ou despertares; portanto, além de ter uma aparência estranha, deve ser suficiente).

Você precisa de um utilitário que funcione indefinidamente, manterá o stdout aberto, mas não gravará nada no stdout e não sairá quando o stdin estiver fechado. Algo como yesrealmente escreve para stdout. catsairá quando o stdin estiver fechado (ou o que você redirecionar para ele estiver pronto). Eu acho que sleep 1000000000dpode funcionar, mas tailé claramente melhor. Minha caixa Debian tem um tailfcomando que encurta um pouco.

Tomando uma abordagem diferente, que tal executar o programa screen?

PT
fonte
Gosto da tail -f /dev/nullabordagem da melhor maneira e também a acho elegante o suficiente, pois o uso do comando corresponde muito bem ao objetivo pretendido.
Jw013 12/07/2012
2
Do strace tail -f /dev/nullparece que tailutiliza inotifye que wakeups ocorrer em casos tolas como sudo touch /dev/null. É triste que parece não haver solução melhor ... Gostaria de saber qual seria o syscall certo para usar para implementar uma solução melhor.
A3nm 12/07/2012
5
@ a3nm O syscall seria pause, mas não é exposto diretamente a uma interface do shell.
Gilles
PT: Eu sei screen, mas isso é para executar várias ocorrências do programa a partir de um script de shell para fins de teste, portanto, usar screené um pouco exagerado.
a3nm
3
@sillyMunky Silly Monkey, a resposta de WaelJ está errada (envia zeros infinitos para stdin).
PT
48

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

Você pode usar infinityporque sleepaceita 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, portanto 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 nas versões mais recentes do Mac - veja comentários).

Zaz
fonte
Haha, essa é realmente uma boa abordagem. :)
a3nm
@ a3nm: Obrigado :) Parece sleep infinitytambém funciona em BSD e Mac .
Zaz
Que tipo de recursos leva um processo infinitamente adormecido? Apenas RAM?
CMCDragonkai
1
Esta resposta afirma que sleep infinityaguarda no máximo 24 dias; quem esta certo?
Nh2 11/11/19
1
@Zaz Investiguei o problema em detalhes agora. Acontece que você estava inicialmente correto! O sleeputilitário não está limitado a 24 dias ; é apenas o primeiro syscall que dorme por 24 dias e depois fará mais desses syscalls. Veja meu comentário aqui: stackoverflow.com/questions/2935183/…
nh2
19
sleep 2147483647 | program > output &

Sim, 2^31-1é um número finito e não durará para sempre , mas darei $ 1000 quando o sono finalmente terminar. (Dica: um de nós estará morto até então.)

  • sem arquivos temporários; Verifica.
  • sem espera ocupada ou despertares periódicos; Verifica
  • sem utilidades exóticas; Verifica.
  • o mais curto possível. Ok, poderia ser mais curto.
Roubar
fonte
5
bash: sleep $ ((64 # 1 _____)) | programa> output &
Diego Torres Milano
Que dorme por 68 anos , este dorme por 98 séculos : sleep 2147483647d...
AGC
9

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

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

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

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

Isso aciona 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 possui 12345 PID, pode kill -CONT 12345fazê-lo continuar.

roaima
fonte
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 canal, fornece a extremidade de leitura do canal, então aqui é o mesmo que no stdin do programa. Então, basicamente, readnunca retornará, porque a única coisa que pode gravar nesse canal é ela mesma e readnão gera nada.

Também funcionará no FreeBSD ou Solaris, mas por outro motivo. Lá, abrir / dev / fd / 1 fornece o mesmo recurso que abrir no fd 1, como você esperaria e como a maioria dos sistemas, exceto o Linux, o que significa que é o fim da escrita. No entanto, no FreeBSD e Solaris, os pipes são bidirecionais. Portanto, contanto programque não escreva em seu stdin (nenhum aplicativo faz), não readhaverá nada para ler nessa direção do pipe.

Nos sistemas em que os pipes não são bidirecionais, readprovavelmente ocorrerá um erro ao tentar ler um descritor de arquivo somente gravação. Observe também que nem todos os sistemas possuem /dev/fd/x.

Stéphane Chazelas
fonte
Muito agradável! De fato, meus testes você não precisa do xbash; Além disso, com o zsh, você pode simplesmente fazer reade funciona (embora eu não entenda o porquê!). Esse truque é específico do Linux ou funciona em todos os sistemas * nix?
A3nm
@ a3nm, se você fizer readsozinho, ele lerá stdin. Portanto, se for o terminal, ele lerá o que você digitar até pressionar Enter.
Stéphane Chazelas
claro, eu entendo o que a leitura faz. O que não entendo é por que a leitura do terminal com a leitura em um processo em segundo plano está bloqueando com o bash, mas não com o zsh.
a3nm
@ a3nm, não sei o que você quer dizer. O que você quer dizer com você pode simplesmente fazer reade funciona ?
Stéphane Chazelas
Estou dizendo que com o zsh você pode simplesmente fazer read | program > outpute funciona da mesma maneira que o que você sugeriu. (E eu não entendo o porquê.)
a3nm
0

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

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

Redirecionar /dev/zerocomo entrada padrão!

program < /dev/zero > output &
WaelJ
fonte
9
Isso daria ao seu programa um número infinito de zero bytes ... o que, infelizmente, o tornaria ocupado.
Jander
1
Isso não é verdadeiro jander, / dev / zero nunca será fechado, mantendo a corrente do tubo aberta. No entanto, o pôster diz que ele não aceita stdin, portanto nenhum zeros será transferido para o programa. Este não é um loop ocupado, é uma espera pura.
sillyMunky
2
desculpe, o OP usa stdin, então isso eliminará sua entrada e será extraído de / dev / zero. Eu deveria ler duas vezes na próxima vez! Se o OP não estivesse usando o stdin, essa seria a solução mais elegante que eu já vi e não seria uma espera ocupada.
sillyMunky