Eu uso o servidor Ubuntu 16.04 e desejo usar o utilitário at
em minha sessão atual para fazer algo daqui a 1 minuto (digamos, um echo
), sem fornecer uma data e hora específicas - apenas 1 minuto à frente da hora atual.
Isso falhou:
echo 'hi' | at 1m
at
A razão pela qual escolho sleep
é porque o sono prejudica a sessão atual e, portanto, é mais adequado para atrasar os comandos em outra sessão, em vez daquela com a qual trabalhamos na maior parte do tempo. AFAIR, at
não se comporta desta forma e não vai atrapalhar minha sessão.
Atualização_1
Pela resposta de Pied Piper, tentei:
(sleep 1m; echo 'hi') &
Eu tenho um problema com este método: O stream "hi" é impresso dentro do meu prompt primário e também adiciona um prompt secundário vazio ( _
) logo abaixo do prompt primário que o contém, veja:
USER@:~# (sleep 1m; echo 'hi') &
[1] 22731
USER@:~# hi
^C
[1]+ Done
Atualização_2
Pela resposta de Peter Corde, tentei:
(sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)
Isso funciona corretamente no Bash 4.4, mas não em algumas versões mais antigas, aparentemente (veja os comentários na resposta). Eu pessoalmente uso o Bash 4.3 em meus próprios ambientes.
O correto no uso é
at <timespec>
, onde<timespec>
é a especificação de tempo. Você também pode usar a-f
opção para especificar o arquivo que contém os comandos a serem executados. Se-f
nenhum redirecionamento for usado, o at irá ler os comandos para executar a partir do stdin (entrada padrão).No seu exemplo, para executar "algum comando" (escolhi "algum comando" para ser "echo hi" no exemplo abaixo) um minuto logo após pressionar enter (agora), o pretendido
<timespec>
a ser usado énow + 1 minute
. Portanto, para obter o resultado desejado com uma solução one-liner, use o comando abaixo:echo 'echo hi' | at now + 1 minute
Isso é suposto (e fará) para executar o
echo hi
comando após um minuto. O problema aqui é que oecho hi
comando será executado pelo comando at em um tty fictício e a saída será enviada para a caixa de correio do usuário (se estiver configurada corretamente - o que requer uma explicação mais extensa sobre como configurar uma caixa de correio em uma máquina unix e não faz parte do escopo desta questão). O resultado final é que você não poderá ver diretamente o resultadoecho hi
no mesmo terminal em que emitiu o comando. A alternativa mais fácil é você redirecioná-lo para "algum arquivo", por exemplo:echo 'echo hi > /tmp/at.out' | at now + 1 minute
No exemplo acima, "some file" é
/tmp/at.out
e o resultado esperado é que o arquivoat.out
em /tmp seja criado após um minuto e contenha o texto 'hi'.Além do
now + 1 minute
exemplo acima, muitas outras especificações de tempo podem ser usadas, mesmo algumas complexas. O comando at é inteligente o suficiente para interpretar os bastante legíveis por humanos, como os exemplos abaixo:now + 1 day
,13:25
,mid‐night
,noon
, ...Consulte a página man de at para obter mais informações sobre as possíveis especificações de tempo, pois as possíveis variações são bastante extensas e podem incluir datas, horas relativas ou absolutas, etc.
Execute o
sleep
em um subshell:Nota: no Linux você pode dar o argumento para dormir em segundos ou com um sufixo s/m/h/d (para segundos, minutos, horas, dias). No BSD o argumento tem que ser em segundos
Não, não está " impresso no seu
stdin
".Se você pressionasse Enter, ele não tentaria executar
hi
como um comando. Só que o plano de fundo foiecho
impressohi
enquanto o cursor estava após o seu prompt.Talvez você queira imprimir uma nova linha inicial , como
(sleep 1m && echo -e '\nhi' &)
. (Ajuste conforme necessário para oecho
seu shell. Ou useprintf
para portabilidade.)Coloquei
&
dentro do subshell para "rejeitar" o trabalho em segundo plano, assim você também evita o "ruído" do[1]+ Done
. Com&&
entre os comandos,&
aplica-se a toda a cadeia de comandos compostos, então ele retorna ao prompt do shell imediatamente, em vez de esperar parasleep
sair como você faria com(sleep 1; echo ... &)
Aqui está o que acontece (com 1 segundo de atraso):
O Bash não sabe que
echo
imprimiu algo, então não sabe que precisa reimprimir seu prompt ou limpar a tela. Você pode fazer isso manualmente comcontrol-L
.Você poderia escrever uma função de shell que faz com que o bash reimprima seu prompt após o
echo
término . Apenas fazerecho "$PS1"
não reproduzirá nenhum caractere já digitado.kill -WINCH $$
no meu sistema obtém o bash para reimprimir sua linha de prompt sem limpar a tela, deixandohi
uma linha sozinha antes do prompt. SIGWINCH é enviado automaticamente quando o tamanho da JANELA muda, e a resposta do bash a ele acontece para fazer o que queremos.Pode funcionar com outros shells, mas não estou tentando torná-lo 100% portátil / POSIX. ( Infelizmente, isso só funciona no bash4.4, não no bash4.3, ou depende de alguma configuração que desconheço )
Você poderia facilmente agrupar isso em uma função de shell que recebe um argumento de suspensão e uma mensagem.
O bash 4.3 não reimprime seu prompt após o SIGWINCH, então isso não funciona lá. Ativei
shopt -s checkwinsize
em ambos os sistemas, mas só funciona no sistema Bash 4.4 (Arch Linux).Se não funcionar, você pode tentar
(sleep 2 && echo -e '\nhi' && echo "$PS1" &)
, que "funciona" se a linha de comando estiver vazia. (Também ignora a possibilidadePROMPT_COMMAND
definida.)Não há uma maneira realmente limpa de fazer com que a saída do terminal se misture com o que você estiver fazendo em seu terminal mais tarde, quando o cronômetro disparar. Se você estiver no meio da rolagem de algo em
less
, poderá facilmente perdê-lo.Se você está procurando algo com resultados interativos, sugiro reproduzir um arquivo de áudio. por exemplo, com um player de linha de comando como
mpv
(bom fork do MPlayer2). Ou escolha um arquivo de vídeo para abrir uma nova janela.Você pode querer
echo
abrir um novo xterm / konsole / gnome-terminal. Use uma opção que mantenha o terminal aberto após a saída do comando.Isso falhou porque você está canalizando a saída de
echo "hi"
, que é apenashi
paraat
, masat
espera que sua entrada possa ser executada pelo shell padrão do sistema (geralmente bash, mas às vezes algo diferente dependendo do sistema).Este:
vai conseguir o que você parece estar tentando fazer. Ele usa uma construção de shell chamada 'documento aqui', que geralmente é a maneira aceita de fazer esse tipo de coisa (porque ele lida com várias linhas melhor do que usar o
echo
comando e não requer a criação de um novo arquivo como você precisaria comcat
) e traduz para o equivalente um pouco mais complicado usandoecho
:Provavelmente vale a pena notar que
at
enviará qualquer saída de comando para o usuário que invocou oat
comando, em vez de enviar a saída para o terminal como um comando atrasado usandosleep
em um subshell faria. Em particular, isso significa que, a menos que você tenha feito alguma personalização do sistema ou pretenda ler seu spool de correio local, não poderá obter a saída dos comandos executados por meio deat
.Combine a resposta de @Peter Cordes e esta resposta https://stackoverflow.com/questions/23125527/how-to-return-to-bash-prompt-after-printing-output-from-backgrounded-function/23125687#23125687
O código abaixo é da última resposta, com uma pequena alteração minha. Compile-o para arquivar a.out (qualquer nome que você quiser)
Em seguida, execute o comando abaixo. (Coloquei a.out no diretório /tmp/, talvez seja necessário alterar para o seu)
ou
BTW,
kill -WINCH $$
funciona muito bem para mim com Bash 4.3 no Gentoo.Com base nas respostas acima, você pode fazer com que o comando incorporado direcione sua saída para o seu terminal, assim:
echo "echo hi >$(tty); kill -WINCH $$" | at now + 1 minute
O
tty
comando retorna o caminho do dispositivo do seu terminal atual, colocando-o no$(...)
bash para executá-lo em um sub-shell, e o comando kill envia um sinal para o processo atual que fará com que o bash reimprima seu prompt $PS1.