em algum momento a partir de agora fazer alguma coisa (e talvez também mostrar resultado no console)

12

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

Isso falhou:

echo 'hi' | at 1m

O motivo pelo qual eu escolho at over sleep é porque o sono prejudica a sessão atual e é, portanto, mais adequado para atrasar comandos em outra sessão, em vez de trabalhar com a maior parte do tempo. AFAIR, at não se comporta dessa maneira e não prejudica minha sessão.

Update_1

Por resposta do Pied Piper, eu tentei:

(sleep 1m; echo 'hi')  &

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

USER@:~# (sleep 1m; echo 'hi')  &
[1] 22731
USER@:~# hi
^C
[1]+  Done

Update_2

Por resposta de Peter Corde eu 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.

    
por Arcticooling 04.12.2017 / 21:00

6 respostas

6

The "hi" stream is printed in my prompt (in the stdin) and not in my stdout

Não, é não "impresso em seu stdin ".

Se você pressionou return, ele não tentaria executar hi como um comando. É só que o% background echo imprimiu hi enquanto o cursor estava após o seu prompt.

Talvez você queira imprimir uma nova linha principal , como (sleep 1m && echo -e '\nhi' &) . (Ajuste conforme necessário para o echo no seu shell. Ou use printf para portabilidade.)

Eu coloquei o & dentro do subshell para "negar" o job em background, então você também evita o "ruído" de [1]+ Done . Com && entre os comandos, & se aplica a toda a cadeia de comandos compostos, por isso retorna imediatamente ao prompt do shell, em vez de esperar que sleep saia como você obteria com (sleep 1; echo ... &)

Veja o que acontece (com atraso de 1 segundo):

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 echo imprimiu algo, por isso não sabe que precisa reimprimir o seu pedido ou limpar o ecrã. 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 echo terminar . Apenas fazendo echo "$PS1" não irá reproduzir nenhum caractere já digitado. kill -WINCH $$ no meu sistema fica boquiaberto para reimprimir sua linha de prompt sem limpar a tela, deixando hi em uma linha sozinha antes do prompt. SIGWINCH é enviado automaticamente quando o tamanho do WINdow é CHANGES, e a resposta do bash a ele faz o que queremos.

Pode funcionar com outros shells, mas eu não estou tentando fazer isso 100% portátil / POSIX. ( Infelizmente, isso só funciona no bash4.4, não no bash4.3, ou depende de alguma configuração que eu desconheç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 de shell que usa um argumento sleep e uma mensagem.

O

bash 4.3 não reimprime seu prompt após o SIGWINCH, então isso não funciona lá. Eu tenho shopt -s checkwinsize ativado 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 possibilidade de que PROMPT_COMMAND esteja definido.)

Não existe uma maneira realmente simples de obter a saída do terminal para mixar com o que você estiver fazendo no seu terminal mais tarde, quando o timer disparar. Se você está no meio da rolagem de algo em less , você pode facilmente perder isso.

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.

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

Você pode querer que seu echo abra um novo xterm / konsole / gnome-terminal. Use uma opção que mantenha o terminal aberto após o comando sair.

    
por 05.12.2017 / 02:04
13

O correto no uso é at <timespec> , onde <timespec> é a especificação de tempo. Você também pode usar a opção -f para especificar o arquivo que contém os comandos a serem executados. Se -f e nenhum redirecionamento forem usados, leia os comandos para executar a partir do stdin (entrada padrão).

No seu exemplo, para executar "some command" (eu escolhi "some command" para ser "echo hi" no exemplo abaixo) um minuto logo após você apertar enter (now), o pretendido <timespec> a ser usado é now + 1 minute . Assim, para obter o resultado desejado com uma solução de uma linha, use o comando abaixo:

echo 'echo hi' | at now + 1 minute

Isto é suposto (e serve) para executar o comando echo hi após um minuto. O problema aqui é que o comando echo hi 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 detalhada sobre como configurar um caixa de correio em uma máquina unix e não faz parte do escopo desta questão). A linha inferior é que você não poderá ver diretamente o resultado de echo hi no mesmo terminal que você 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, "algum arquivo" é /tmp/at.out e o resultado esperado é que o arquivo at.out sob / tmp seja criado após um minuto e contenha o texto 'hi'.

Além do exemplo now + 1 minute acima, muitas outras especificações de tempo podem ser usadas, mesmo algumas complexas. O comando at é inteligente o suficiente para interpretar os que são bastante legíveis, como os exemplos abaixo:

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

Por favor, consulte a página de manual do at para mais sobre as possíveis especificações de tempo, pois as variações possíveis são bastante extensas e podem incluir datas, horários relativos ou absolutos, etc.

    
por 04.12.2017 / 21:40
7

Execute o sleep em um subshell:

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

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

    
por 04.12.2017 / 21:22
3

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

Isto:

at 1m << EOF
echo "hi"
EOF

vai conseguir o que parece estar tentando fazer. Ele usa uma construção de shell chamada 'here document', que geralmente é a maneira aceita de fazer esse tipo de coisa (porque ele manipula várias linhas melhor do que usar o comando echo e não requer a criação de um novo arquivo como você faria precisa com cat ), e traduz o equivalente um pouco mais complicado usando echo :

echo 'echo "hi"' | at 1m

Provavelmente, vale a pena notar que at enviará qualquer saída de comando para o usuário que chamou o comando at , em vez de enviar a saída para o terminal como um comando atrasado usando sleep em um subshell. Em particular, isso significa que, a menos que você tenha feito alguma customização do sistema ou tenha a intenção de ler o spool de correio local, não será possível obter a saída dos comandos executados via at .

    
por 04.12.2017 / 21:34
2

Combine a resposta do @Peter Cordes e esta resposta link

O código abaixo é da última resposta, com uma pequena alteração minha. Compile-o para arquivar a.out (qualquer que seja o nome que você goste)

#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. (Eu coloquei a.out em dir / tmp /, você pode precisar 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.

    
por 08.12.2017 / 10:22
1

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

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

O comando tty retorna o caminho do dispositivo do seu terminal atual, colocando-o em $(...) e o comando kill envia um sinal para o processo atual que terá o retorno imprima seu prompt $ PS1.

    
por 08.12.2017 / 05:17