daqui a algum tempo, faça alguma coisa (e talvez também mostre o resultado no console)

12

Eu uso o servidor Ubuntu 16.04 e desejo usar o utilitário atna minha sessão atual para fazer algo daqui a 1 minuto (digamos, um echo), sem fornecer uma data e hora específicas - apenas um minuto antes do horário atual.

Isso falhou:

echo 'hi' | at 1m

A razão que eu escolher atmais sleepé porque desvantagens sono sessão atual e é para esse fim mais adequado aos comandos de atraso em outra sessão, em vez do trabalho que nós com a maior parte do tempo. AFAIR, atnão se comporta dessa maneira e não prejudica minha sessão.

Update_1

Pela resposta de Pied Piper, tentei:

(sleep 1m; echo 'hi')  &

Estou com um problema com este método: O fluxo "oi" é impresso dentro do meu prompt principal e também adiciona um prompt secundário vazio (_ ) logo abaixo do prompt principal que o contém, consulte:

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.

Arcticooling
fonte
2
Eu gostaria que as bandeiras de morte fossem no estilo GNU e pudessem ser reorganizadas para que não parecesse "matar a moça"!
NH.

Respostas:

6

O fluxo "oi" é impresso no meu prompt (no stdin) e não no meustdout

Não, é não "impresso na sua stdin".

Se você pressionasse return, ele não tentaria executar hicomo um comando. É que o plano de fundo foi echoimpresso hienquanto o cursor estava após o seu prompt.


Talvez você queira imprimir uma nova linha líder , como (sleep 1m && echo -e '\nhi' &). (Ajuste conforme necessário para oecho seu shell. Ou use printfpara portabilidade.)

Eu coloquei o &interior do subshell para "negar" o trabalho em segundo plano, para que você também evite o "ruído" de [1]+ Done. Com &&entre os comandos, &aplica-se a toda a cadeia composto de comando, assim que ele retorna para a direita janela de comandos para longe em vez de esperar sleeppara sair como você deseja obter com(sleep 1; echo ... &)

Aqui está o que acontece (com 1 segundo de atraso):

peter@volta:~$ (sleep 1 && echo -e '\nhi' &)
peter@volta:~$ 
hi
       # cursor is at the start of this empty line.

O Bash não sabe que echoimprimiu alguma coisa, por isso não sabe que precisa reimprimir o prompt ou limpar a tela. Você pode fazer isso manualmente com control-L.

Você pode escrever uma função de shell que recebe o bash para reimprimir seu prompt após a echoconclusão . Apenas fazer echo "$PS1"não reproduzirá nenhum caractere já digitado. kill -WINCH $$no meu sistema, o bash obtém o reimprimir sua linha de prompt sem limpar a tela, deixando hiuma linha sozinha antes do prompt. O SIGWINCH é enviado automaticamente quando o tamanho do WINdow muda, e a resposta do bash faz o que queremos.

Pode funcionar com outras conchas, mas não estou tentando tornar este 100% portátil / POSIX. ( Infelizmente, isso só funciona no bash4.4, não no bash4.3, ou depende de alguma configuração da qual não conheço )

  # bash4.4
peter@volta:~$ (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)
peter@volta:~$ ljlksjksajflasdf dfas      # typed in 2 seconds
hi
peter@volta:~$ ljlksjksajflasdf dfas      # reprinted by Bash because of `kill -WINCH`

Você pode facilmente agrupar isso em uma função shell que recebe uma mensagem de suspensão e uma mensagem de suspensão.

o bash 4.3 não reimprime seu prompt após o SIGWINCH; portanto, isso não funciona lá. eu tenhoshopt -s checkwinsize ativei nos dois sistemas, mas ele funciona apenas no sistema Bash 4.4 (Arch Linux).

Se não funcionar, você pode tentar o (sleep 2 && echo -e '\nhi' && echo "$PS1" &)que "funciona" se a linha de comando estiver vazia. (Ele também ignora a possibilidade PROMPT_COMMANDdefinida).


Não há uma maneira realmente limpa de fazer com que a saída do terminal se misture com o que você possa estar fazendo no seu terminal mais tarde, quando o timer disparar. Se você estiver no meio de rolar alguma coisaless , poderá facilmente perdê-la.

Se você estiver procurando algo com resultados interativos, sugiro que você reproduza um arquivo de áudio. por exemplo, com um jogador de linha de comando como mpv(belo fork do MPlayer2). Ou escolha um arquivo de vídeo para abrir uma nova janela.

(sleep 1 && mpv /f/share/music/.../foo.ogg </dev/null &>/dev/null &)

Você pode querer echoabrir um novo xterm / konsole / gnome-terminal. Use uma opção que mantém o terminal aberto após a saída do comando.

Peter Cordes
fonte
Agradeço-lhe caro David, e já me inscrevi. Este é realmente o mais próximo que cheguei em relação ao que procuro. Existe uma maneira de atualizar o comando a seguir para emular um Enterevento de pressionamento de tecla, logo após o eco aparecer, para que eu retorne automaticamente ao prompt principal do console ? (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &).
Arcticooling
@Arcticooling: kill -WINCH $$ não atualizar o prompt do shell em execução no terminal. Esse era o objetivo de usá-lo. Pressionar enter enviaria um comando parcialmente digitado, mas WINCH não, como mostrei. Se você não digitou nada, receberá um prompt vazio.
Peter Cordes
1
Se você começou a digitar rm -rf ~/some/directory, a opção Enter na hora errada poderia ser executada rm -rf ~/. (Segurança dica: caminhos de digitação de acabamento e , em seguida, controlar-a e mudança lspara rm -rf, tão gordo-dedilhado entrar a qualquer momento não vai estragar o seu dia.)
Peter Cordes
Com fio, executei (sleep 2 && echo -e '\nhi' && kill -WINCH $$ &)e, depois que o eco apareceu, recebi um prompt vazio, somente depois de bater Entervoltei ao prompt principal ... Desculpe se eu entendi errado, sou completamente novo no Bash.
Arcticooling
@ Arcticooling: Estou usando o Bash 4.4 no Arch Linux, em um emulador de terminal do Konsole. Eu esperava que meu comando fosse portátil pelo menos para outras versões do bash, mas talvez não :( Sim, acabei de testar no Bash 4.3 dentro de uma screensessão (e apenas através do SSH) em um sistema Ubuntu mais antigo, e recebi o comportamento que você descreve.
Peter Cordes
13

O correto no uso é at <timespec>onde <timespec>está a especificação de tempo. Você também pode usar a -fopção para especificar o arquivo que contém os comandos a serem executados. Se -fnenhum redirecionamento for usado, o at lerá os comandos a serem executados a partir do stdin (entrada padrão).

No seu exemplo, para executar "algum comando" (eu escolhi "algum comando" para ser "eco oi" no exemplo abaixo) um minuto depois de pressionar enter (agora), o pretendido <timespec>a ser usado é now + 1 minute. Portanto, para obter o resultado desejado com uma solução de uma linha, use o comando abaixo:

echo 'echo hi' | at now + 1 minute

Isso deve (e servirá) para executar o echo hicomando após um minuto. O problema aqui é que o echo hicomando será executado pelo comando at em um dummy tty 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 no uma máquina unix e não faz parte do escopo desta questão). O ponto principal é que você não poderá ver diretamente o resultado echo hino mesmo terminal em que emitiu o comando. A alternativa mais fácil é redirecioná-lo para "algum arquivo", por exemplo:

echo 'echo hi > /tmp/at.out' | at now + 1 minute

No exemplo acima, "some file" é /tmp/at.oute o resultado esperado é que o arquivo at.outem / tmp seja criado após um minuto e contenha o texto 'oi'.

Além do now + 1 minuteexemplo acima, muitas outras especificações de tempo podem ser usadas, mesmo algumas complexas. O comando at é inteligente o suficiente para interpretar legíveis muito humanas, como nos exemplos abaixo:

now + 1 day, 13:25, mid‐night, noon, ...

Consulte a página de manual do at para obter mais informações sobre as possíveis especificações de horário, pois as variações possíveis são bastante extensas e podem incluir datas, horas relativas ou absolutas, etc.

Marcelo
fonte
2
atpor padrão, envia a saída por email, mas isso precisa ser configurado corretamente para funcionar.
Simon Richter
Agora ofereço uma recompensa de 100 representantes por uma resposta. Por favor, leia minha mensagem de recompensa abaixo e edite para explicar mais sobre o que é at <timespec>e a bandeira que você usou e se é compatível com o Ubuntu 16.04 xenial.
Arcticooling
Ok, eu aumentei o nível de detalhe na resposta para que agora seja mais didático. Pode parecer um pouco longo para alguns, mas acredito que não deixa margem para dúvidas, pois cita claramente exemplos que funcionam e como funcionam também.
Marcelo
7

Execute o sleepsubshell:

(sleep 60; echo -e '\nhi')  &

Nota: no Linux, você pode dar o argumento para dormir em segundos ou com o sufixo s / m / h / d (por segundos, minutos, horas, dias). No BSD, o argumento deve ser em segundos

PiedPiper
fonte
Eu tenho um pequeno problema com esse método - o fluxo "oi" é impresso no meu prompt (no stdin) e não no meustdout (como faria com um regular echo).
Arcticooling
1
Se você não deseja que a saída do seu terminal seja misturada com prompts e a saída de outros comandos, será necessário redirecioná-la para algum lugar
PiedPiper
Falha no redirecionamento @PiedPiper (sleep 10s; echo 'hi') & 1>. Agora eu li mais sobre isso.
Arcticooling 4/17/17
2
Ler a documentação é sempre uma boa ideia :)
PiedPiper
1
@ Arcticooling Parece que você está confuso sobre os significados de stdine stdout. Eles não têm nada a ver com o local onde as coisas aparecem no seu terminal; em vez disso, você deve interpretá-los com relação a um processo específico (basicamente, um programa). A entrada padrão ("stdin") para um processo é uma fonte a partir da qual o processo pode ler dados, e a saída padrão do processo ("stdout") é um destino no qual ele pode gravar dados. Essas fontes e destinos podem ser outros programas, arquivos ou o próprio terminal (as coisas que você digita vão para o stdin e tudo o que vem ao stdout é exibido).
David Z
3

Isso falhou porque você está canalizando a saída de echo "hi", que é apenas hipara at, masat espera que sua entrada possa ser executada pelo shell do sistema padrão (geralmente bash, mas às vezes algo diferente dependendo do sistema).

Este:

at 1m << EOF
echo "hi"
EOF

alcançará o que você parece estar tentando fazer. Ele usa uma construção de shell chamada 'aqui document', que geralmente é a maneira aceita de fazer esse tipo de coisa (porque lida com várias linhas melhor do que usar o echocomando e não requer a criação de um novo arquivo como você precisaria com cat) e traduz para o equivalente um pouco mais complicado usando echo:

echo 'echo "hi"' | at 1m

Provavelmente vale a pena notar que atenviará qualquer saída de comando para o usuário que atchamou o comando, em vez de direcionar a saída para o terminal como um comando atrasado usandosleep em um subshell. Em particular, isso significa que, a menos que você tenha feito alguma customização do sistema ou pretenda ler seu spool de correio local, não poderá executar a saída de comandos at.

Austin Hemmelgarn
fonte
Por alguma razão, o exemplo que você deu me deu um erro syntax error. Last token seen: m Garbled time . Eu não tenho ideia do porquê. Eu uso o Ubuntu 16.04, Bash 4.0. Talvez você tenha feito isso em outro sistema. Mesmo quando digito atsem mais argumentos - ainda recebo o mesmo erro. Mais dados aqui .
Arcticooling
1
at 1mnão funciona para um posix compatível at. O comando precisaria ser #at now + 1 minute
PiedPiper
O @PiedPiper está correto, 1m não é compatível com versões compatíveis com POSIX at, eu estava assumindo que você tinha uma versão que aceitava essa sintaxe.
Austin Hemmelgarn
O @PiedPiper at 1mnão é POSIX, mas as implementações compatíveis com POSIX atsão livres para interpretá-lo como desejarem, não torna uma atimplementação menos compatível interpretá-lo como o mesmo now + 1 minuteque retornar um erro ou um mês atrás . IOW, é o at 1mcódigo que não é POSIX.
Stéphane Chazelas
2

Combine a resposta de Peter Cordes e esta resposta /programming/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)

#include <sys/ioctl.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
        char *tty = "/dev/tty";
        if (argc > 1)
                tty = argv[1];
        int fd = open(tty, O_WRONLY);

        int i;
        char buf[] = "\n";
        for (i = 0; i < sizeof buf - 1; i++)
                if (ioctl(fd, TIOCSTI, &buf[i]))
                        perror("ioctl");
        close(fd);
        return 0;
}

Em seguida, execute o comando abaixo. (Coloquei a.out em dir / tmp /, pode ser necessário mudar para o seu)

(sleep 2 && echo -e '\nhi' && /tmp/a.out &)

ou

(sleep 2 && echo -e '\nhi' && /tmp/a.out /proc/$$/fd/0 &)

BTW, kill -WINCH $$funciona muito bem para mim com o Bash 4.3 no Gentoo.

Bruce
fonte
1

Com base nas respostas acima, você pode fazer com que o comando incorporado direcione sua saída para o seu terminal, da seguinte maneira:

echo "echo hi >$(tty); kill -WINCH $$" | at now + 1 minute

O ttycomando retorna o caminho do dispositivo do seu terminal atual, envolvendo-o $(...)com o bash e executando-o em um sub-shell, e o comando kill envia um sinal para o processo atual que terá o bash reimprimir seu prompt $ PS1.

user1404316
fonte