Premere un tasto qualsiasi per mettere in pausa lo script della shell, premere di nuovo per riprendere


10

Ho scritto uno script di shell per testare un'API che copia i file e fa eco ai suoi progressi dopo ognuno.

C'è una pausa di due secondi tra ogni copia, quindi vorrei aggiungere la possibilità di premere qualsiasi tasto per mettere in pausa lo script per consentire test più approfonditi. Quindi premere un tasto qualsiasi per riprendere.

Come posso aggiungerlo nel minor numero di righe possibile?

Risposte:


12

Non è necessario aggiungere qualcosa al tuo script. La shell consente tale funzionalità.

  • Inizia la tua sceneggiatura in un terminale.
  • Mentre è in esecuzione e blocca l'utilizzo del terminale ctrl- z. Il terminale viene nuovamente rilasciato e viene visualizzato un messaggio di arresto del processo. (Ora è nello stato porcess T, arrestato)
  • Ora fai quello che vuoi. Puoi anche avviare altri processi / script e interromperli con ctrl- z.
  • Digitare jobsil terminale o elencare tutti i lavori arrestati.
  • Per consentire allo script di continuare, digitare fg(primo piano). Riprende il lavoro nel gruppo di processi in primo piano e i lavori continuano a essere eseguiti.

Vedi un esempio:

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

come hai detto tu, se corro sleep 10; notify-send helloe premo CTRL + Z per fermarmi, fatti notify-send helloeseguire. se il secondo comando viene eseguito come mai il primo processo viene interrotto? dopo di che, se non fgriesco a vedere nulla, è ovvio, dato che il secondo comando è già eseguito
Edward Torvalds,

@edwardtorvalds perché i due comandi sono separati. In una sceneggiatura dovrebbero essere in una subshell. Scrivili in uno script semplice ed esegui lo script. Quindi ctrl-z per interrompere e vedrai che il secondo comando non viene eseguito fino al termine del primo. Scrivere cmd; cmd; cmd;è come scrivere cmd <newline> cmd <newline> .... In alternativa a uno script che puoi scrivere ( cmd; cmd; cmd; ), si comporterebbe come lo script, parte della subshell generata da(
caos,

ho anche provato sleep 10. quando premo CTRL + Z dopo 3 secondi e riprendo dopo pochi secondi e ho notato che il comando sleep è morto in meno di 7 secondi. che è l'opposto di quello che hai detto, poiché il comando nevers viene fermato, gira solo in background.
Edward Torvalds,

@edwardtorvalds L'ho notato anch'io ... Non ha senso. Ho detto straceil sleepcomando e ho scoperto che la chiamata di sistema utilizzata è nanosleep(). Sembra essere un comportamento definito della chiamata di nanosleepsistema. restart_syscall()riavvia la chiamata di sistema interrotta con un argomento time che viene opportunamente adattato per tenere conto del tempo che è già trascorso (incluso il tempo in cui il processo è stato interrotto da un segnale). Leggi quella manpage: man7.org/linux/man-pages/man2/restart_syscall.2.html
caos

Diverse note per completare la risposta di @chaos (sentitevi liberi di includerle): quando si fa ctrl-Z, il lavoro viene sospeso ("arrestato"), quindi al momento non è in esecuzione, in attesa che decidiate di continuare in primo piano (es:) fg %1o sullo sfondo (es:) bg %1. (se i lavori danno solo 1 numero, cioè solo 1 processo sospeso, come mostrato nel caos di esempio: solo [1]+ stopped sleep 10, è possibile omettere la %nparte. Se ci sono diversi processi in background (in esecuzione o arrestati), è necessario designare quello desiderato con: %n(es: fg %2 per riprendere% 2 in primo piano))
Olivier Dulac

6

Se vuoi solo mettere in pausa lo script rimanendo all'interno dello script, puoi usare read invece di sleep.

Puoi usare

read -tper impostare un timeout affinché la lettura
read -nlegga un carattere (in effetti basta premere un tasto qualsiasi) per continuare lo script

Poiché non hai fornito alcun codice, di seguito è riportato un esempio di come potrebbe essere utilizzato.
Se si preme q, si read -n1impedisce allo script di continuare fino a quando non viene premuto un tasto.
Quando viene premuto un tasto, il segno di spunta viene ripristinato e lo script continua nel ciclo 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

Puoi anche aggiungere stty -echoall'inizio della sezione e stty echoalla fine per evitare che la digitazione rovini l'output dello schermo


@mikeserv Non credo che gli interessi a ciò che viene letto, vogliono solo mettere in pausa e riprendere lo script, cosa intendi anche per salvare le impostazioni del terminale, ne sto cambiando solo uno, e questo sembra un po 'eccessivo.

1
@mikeserv ahh, stavo solo supponendo che tutto lo stdin sarebbe arrivato dall'utente.

1

Con ddte puoi leggere in modo affidabile un singolo byte da un file. Con sttyè possibile impostare un minnumero di byte per qualificare una lettura del terminale e timeun'uscita in decimi di secondo. Combina quei due e ne puoi fare a meno sleep, penso, e lascia che il timeout di lettura del terminale faccia il lavoro per te:

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

Questo è un piccolo esempio whileche ho preso in giro per farti provare. Ogni due secondi ddscade il tentativo di leggere stdin- reindirizzato da /dev/tty- e il whileloop scorre. Questo o dd non si interrompe perché si preme un tasto, nel qual caso viene invocata una shell interattiva.

Ecco una prova: i numeri stampati all'inizio di ogni riga rappresentano il valore della variabile 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

Non dovresti usarlo stty sanedopo aver modificato le impostazioni stty, potrei sbagliarmi ma non sembra che le abbia ripristinate da nessuna parte?

@Jidder - no, salvo lo stato del terminale nella parte superiore dello script s=$(stty -g </dev/tty). Immediatamente dopo aver chiamato, ddlo ripristino con stty "$s". Lo stato del terminale non si preoccupa dei sottotitoli e quindi tali impostazioni restano indipendenti dalla shell madre. stty sanenon è necessariamente quello che vuoi fare - è meglio ripristinare lo stato nel modo in cui l'hai trovato piuttosto che supporre che lo stato fosse sanea quel punto. Se non lo ripristinassi, quelli echosarebbero dappertutto. Capirlo è in parte il motivo per cui sono arrivato così tardi - la tua risposta non è stata qui quando ho iniziato i test.
Mikeserv,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.