Pressione qualquer tecla para pausar o shell script, pressione novamente para continuar


10

Eu escrevi um script de shell para testar uma API que copia arquivos e ecoa seu progresso após cada um.

Há um intervalo de dois segundos entre cada cópia, então eu gostaria de adicionar a capacidade de pressionar qualquer tecla para pausar o script para permitir testes mais aprofundados. Em seguida, pressione qualquer tecla para continuar.

Como posso adicionar isso no menor número de linhas possível?

Respostas:


12

Você não precisa adicionar algo ao seu script. O shell permite essa funcionalidade.

  • Inicie seu script em um terminal.
  • Enquanto estiver em execução e bloqueando o uso do terminal ctrl- z. O terminal é liberado novamente e você vê uma mensagem de que o processo está parado. (Agora está no estado porcess T, parado)
  • Agora faça o que quiser. Você também pode iniciar outros processos / scripts e pará-los com ctrl- z.
  • Digite jobso terminal ou liste todos os trabalhos interrompidos.
  • Para deixar seu script continuar, digite fg(primeiro plano). Ele retoma o trabalho de volta ao grupo de processos em primeiro plano e os trabalhos continuam em execução.

Veja um exemplo:

root@host:~$ sleep 10 # sleep for 10 seconds
^Z
[1]+  Stopped                 sleep 10
root@host:~$ jobs # list all stopped jobs
[1]+  Stopped                 sleep 10
root@host:~$ fg # continue the job
sleep 10
root@host:~$ # job has finished

como você disse, se eu correr sleep 10; notify-send helloe pressionar CTRL + Z para parar, notify-send helloseja executado. se o segundo comando está sendo executado, como é que o primeiro processo é interrompido? depois disso, se o tipo fgeu não posso ver acontecendo nada, o que é óbvio, já que segundo comando já é executado
Edward Torvalds

@edwardtorvalds porque os dois comandos são separados. Em um script, eles estariam em um subshell. Escreva-os em um script simples e execute o script. Em seguida, pressione Ctrl-Z para parar e você verá que o segundo comando não é executado até que o primeiro termine. Escrever cmd; cmd; cmd;é como escrever cmd <newline> cmd <newline> .... Como alternativa a um script que você pode escrever ( cmd; cmd; cmd; ), ele se comportaria como o script, por causa do subshell gerado por(
caos

eu também tentei sleep 10. quando pressiono CTRL + Z após 3 segundos e retomou após alguns segundos e percebi que o comando de suspensão morreu em menos de 7 segundos. o que é oposto ao que você disse, uma vez que os nevos de comando são interrompidos, ele é executado em segundo plano.
Edward Torvalds

@edwardtorvalds Notei isso também ... Não faz sentido. Eu dei straceo sleepcomando e descobri que é a chamada do sistema nanosleep(). Parece ser um nanosleepcomportamento definido da chamada do sistema. restart_syscall()reinicia a chamada interrompida do sistema com um argumento de tempo que é ajustado adequadamente para contabilizar o tempo que já decorreu (incluindo o tempo em que o processo foi interrompido por um sinal). Leia essa página de manual: man7.org/linux/man-pages/man2/restart_syscall.2.html
caos

Várias notas para completar a resposta do @chaos (sinta-se à vontade para incluí-las): quando se pressiona Ctrl-Z, o trabalho é suspenso ("parado"), portanto não está sendo executado no momento, esperando que você decida continuar. em primeiro plano (ex .:) fg %1ou em segundo plano (ex:) bg %1. (se os trabalhos fornecerem apenas 1 número, ou seja, apenas 1 processo suspenso, como no exemplo ilustrado do caos: apenas [1]+ stopped sleep 10, você poderá omitir a %nparte. se houver vários processos em segundo plano (em execução ou interrompidos), será necessário designar o que você deseja com: %n(ex: fg %2 ter% 2 retomado em primeiro plano))
Olivier Dulac

6

Se você quiser apenas pausar o script enquanto permanecer dentro do script, poderá usar a leitura em vez de dormir.

Você pode usar

read -tpara definir um tempo limite para a leitura
read -nler um caractere (basta pressionar qualquer tecla) para continuar o script

Como você não forneceu nenhum código, abaixo está um exemplo de como ele pode ser usado.
Se q for pressionado, read -n1impede que o script continue até que uma tecla seja pressionada.
Quando uma tecla é pressionada, a verificação é redefinida e o script continua no loop normalmente.

while [[ true ]]; do
    read -t2 -n1 check
    if [[ $check == "q" ]];then
        echo "pressed"
        read -n1
        check=""
    else
        echo "not pressed"
    fi
echo "Doing Something"
done

Você também pode adicionar stty -echoao início da seção e stty echoao final para evitar que a digitação atrapalhe a saída da tela


@mikeserv Acho que não se importa com o que é lido, eles apenas querem pausar e retomar o script, o que você quer dizer com salvar as configurações do terminal também, só estou mudando uma para parecer um exagero.

1
@ mikeserv ahh certo, eu estava apenas presumindo que todos os stdin viriam do usuário.

1

Com ddvocê, você pode ler com segurança um único byte de um arquivo. Com sttyvocê pode definir um minnúmero de bytes para qualificar uma leitura de terminal e uma timesaída em décimos de segundo. Combine esses dois e você pode ficar sem sleeptotalmente, eu acho, e deixe o tempo limite de leitura do terminal fazer o trabalho para você:

s=$(stty -g </dev/tty)
(while stty raw -echo isig time 20 min 0;test -z "$(
dd bs=1 count=1 2>/dev/null; stty "$s")" || (exec sh)
do echo "$SECONDS:" do your stuff here maybe                             
   echo  no sleep necessary, I think                                                          
   [ "$((i+=1))" -gt 10 ] && exit                                                             
done       
) </dev/tty

Esse é um pequeno exemplo de whileloop que eu criei para você experimentar. A cada dois segundos, o ddtempo limite é excedido na tentativa de leitura stdin- redirecionada de /dev/tty- e o whileloop é repetido. O tempo limite é excedido ou dd não porque você pressiona uma tecla - nesse caso, um shell interativo é chamado.

Aqui está uma execução de teste - os números impressos no início de cada linha são o valor da variável shell $SECONDS:

273315: do your stuff here maybe
no sleep necessary, I think
273317: do your stuff here maybe
no sleep necessary, I think
273319: do your stuff here maybe
no sleep necessary, I think
273321: do your stuff here maybe
no sleep necessary, I think
sh-4.3$ : if you press a key you get an interactive shell
sh-4.3$ : this example loop quits after ten iterations
sh-4.3$ : or if this shell exits with a non-zero exit status
sh-4.3$ : and speaking of which, to do so you just...
sh-4.3$ exit
exit
273385: do your stuff here maybe
no sleep necessary, I think
273387: do your stuff here maybe
no sleep necessary, I think
273389: do your stuff here maybe
no sleep necessary, I think
273391: do your stuff here maybe
no sleep necessary, I think
273393: do your stuff here maybe
no sleep necessary, I think
273395: do your stuff here maybe
no sleep necessary, I think
273397: do your stuff here maybe
no sleep necessary, I think

Você não deveria usar stty sanedepois de alterar a configuração stty, posso estar errado, mas não parece que você as redefiniu em qualquer lugar?

@ Jidder - não, eu salvo o estado do terminal na parte superior do script com s=$(stty -g </dev/tty). Imediatamente após ligar dd, restauro-o com stty "$s". O estado do terminal não se importa com subshells e, portanto, essas configurações permanecem independentemente do shell pai. stty sanenão é necessariamente o que você quer fazer - é melhor restaurar o estado da maneira que você o encontrou do que assumir que o estado estava sanenaquele ponto. Se eu não estivesse restaurando, esses echoitens estariam por toda parte. Descobrir isso é em parte o motivo de eu ter chegado tão tarde - sua resposta não estava aqui quando comecei a testar.
mikeserv
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.