Drücken Sie eine beliebige Taste, um das Shell-Skript anzuhalten, und drücken Sie erneut, um fortzufahren


10

Ich habe ein Shell-Skript zum Testen einer API geschrieben, die Dateien kopiert und ihren Fortschritt nach jeder einzelnen wiedergibt.

Zwischen jeder Kopie liegt ein Ruhezustand von zwei Sekunden. Daher möchte ich die Möglichkeit hinzufügen, eine beliebige Taste zu drücken, um das Skript anzuhalten und tiefere Tests zu ermöglichen. Drücken Sie dann eine beliebige Taste, um fortzufahren.

Wie kann ich dies in so wenigen Zeilen wie möglich hinzufügen?

Antworten:


12

Sie müssen Ihrem Skript nichts hinzufügen. Die Shell ermöglicht eine solche Funktionalität.

  • Starten Sie Ihr Skript in einem Terminal.
  • Während läuft und blockiert die Terminalnutzung ctrl- z. Das Terminal wird wieder freigegeben und Sie sehen eine Meldung, dass der Prozess gestoppt wurde. (Es ist jetzt im Porzesszustand T, gestoppt)
  • Jetzt mach was du willst. Sie können auch andere Prozesse / Skripte starten und mit ctrl- stoppen z.
  • Geben Sie jobsdas Terminal ein oder listen Sie alle gestoppten Jobs auf.
  • Geben Sie fg(Vordergrund) ein , damit Ihr Skript fortgesetzt werden kann . Der Job wird wieder in die Vordergrundprozessgruppe aufgenommen, und die Jobs werden weiterhin ausgeführt.

Siehe ein Beispiel:

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

Wie Sie sagten, wenn ich renne sleep 10; notify-send hellound STRG + Z drücke, notify-send helloum anzuhalten, werde ich ausgeführt. Wenn der zweite Befehl ausgeführt wird, warum wird der erste Prozess gestoppt? danach, wenn Typ, kann fgich nicht sehen, dass etwas passiert, was offensichtlich ist, da der zweite Befehl bereits ausgeführt wird
Edward Torvalds

@edwardtorvalds, da die beiden Befehle getrennt sind. In einem Skript sollten sie sich in einer Unterschale befinden. Schreiben Sie sie in ein einfaches Skript und führen Sie das Skript aus. Halten Sie dann Strg-Z an, um anzuhalten, und Sie werden sehen, dass der zweite Befehl erst ausgeführt wird, wenn der erste beendet ist. Schreiben cmd; cmd; cmd;ist wie Schreiben cmd <newline> cmd <newline> .... Alternativ zu einem Skript , das Sie schreiben können ( cmd; cmd; cmd; ), ist es wie das Drehbuch verhalten würde, bacuse der Subshell erzeugt durch(
Chaos

Ich habe es auch versucht sleep 10. wenn ich nach 3 Sekunden STRG + Z drücke und nach wenigen Sekunden wieder aufnehme und bemerke, dass der Schlafbefehl in weniger als 7 Sekunden gestorben ist. Das ist im Gegensatz zu dem, was Sie gesagt haben, da der Befehl nevers gestoppt wird, läuft er nur im Hintergrund.
Edward Torvalds

@edwardtorvalds Das ist mir auch aufgefallen ... macht keinen Sinn. Ich hatte straceden sleepBefehl und fand heraus, dass der verwendete Systemaufruf ist nanosleep(). Es scheint ein definiertes Verhalten des Systemaufrufs zu sein nanosleep. restart_syscall()Startet den unterbrochenen Systemaufruf mit einem Zeitargument neu, das entsprechend angepasst wird, um die bereits verstrichene Zeit zu berücksichtigen (einschließlich der Zeit, zu der der Prozess durch ein Signal gestoppt wurde). Lesen Sie diese Manpage: man7.org/linux/man-pages/man2/restart_syscall.2.html
Chaos

Mehrere Notizen zum Vervollständigen der Antwort von @chaos (Sie können sie gerne einschließen): Wenn Sie Strg-Z ausführen, wird der Job angehalten ("gestoppt"), sodass er im Moment nicht ausgeführt wird und darauf wartet, dass Sie sich entscheiden, ihn fortzusetzen im Vordergrund (Bsp. :) fg %1oder im Hintergrund (Bsp bg %1. :) . (Wenn Jobs nur 1 Nummer ausgeben, dh nur 1 angehaltenen Prozess, wie im Beispiel des Chaos gezeigt: Nur [1]+ stopped sleep 10können Sie das %nTeil weglassen . Wenn mehrere Hintergrundprozesse (ausgeführt oder gestoppt) vorhanden sind, müssen Sie den gewünschten angeben mit: %n(zB: fg %2 % 2 wieder im Vordergrund haben))
Olivier Dulac

6

Wenn Sie das Skript nur anhalten möchten, während Sie im Skript verbleiben, können Sie read anstelle von sleep verwenden.

Sie können verwenden

read -tum eine Zeitüberschreitung für das Lesen festzulegen
read -n, um ein Zeichen zu lesen (drücken Sie einfach eine beliebige Taste), um das Skript fortzusetzen

Da Sie keinen Code angegeben haben, finden Sie unten ein Beispiel für die Verwendung.
Wenn q gedrückt wird, wird read -n1verhindert, dass das Skript fortgesetzt wird, bis eine Taste gedrückt wird.
Wenn eine Taste gedrückt wird, wird die Prüfung zurückgesetzt und das Skript wird wie gewohnt in der Schleife fortgesetzt.

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

Sie können auch stty -echoam Anfang des Abschnitts und stty echoam Ende hinzufügen , um zu verhindern, dass die Eingabe die Bildschirmausgabe durcheinander bringt


@mikeserv Ich glaube nicht, dass es op interessiert, was gelesen wird, sie wollen nur pausieren und das Skript fortsetzen. Was meinst du mit Speichern der Terminaleinstellungen? Ich ändere nur eine, so dass das etwas übertrieben erscheint.

1
@mikeserv ahh richtig, ich habe nur angenommen, dass alle stdin vom Benutzer kommen würden.

1

Mit können ddSie zuverlässig ein einzelnes Byte aus einer Datei lesen. Mit können sttySie eine minAnzahl von Bytes festlegen , um einen Terminal-Lesevorgang und einen timeOut in Zehntelsekunden zu qualifizieren . Kombinieren Sie diese beiden und Sie können sleepganz darauf verzichten, denke ich, und lassen Sie einfach die Lesezeitüberschreitung des Terminals die Arbeit für Sie erledigen:

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

Das ist eine kleine Beispielschleife while, die ich verspottet habe, damit Sie sie ausprobieren können. Alle zwei Sekunden tritt eine ddZeitüberschreitung beim versuchten Lesen von stdin- umgeleitet von /dev/tty- und die whileSchleifenschleifen auf. Das oder dd nicht Time-out , weil Sie eine Taste drücken - in diesem Fall eine interaktive Shell aufgerufen wird.

Hier ist ein Testlauf - die am Kopf jeder Zeile aufgedruckten Zahlen sind der Wert der Shell-Variablen $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

Sollten Sie stty sanenach dem Ändern der Stty-Einstellung nicht verwenden, kann ich mich irren, aber es sieht nicht so aus, als würden Sie sie irgendwo zurücksetzen?

@Jidder - nein Ich speichere den Status des Terminals oben im Skript mit s=$(stty -g </dev/tty). Unmittelbar nach dem Anruf ddstelle ich es dann mit wieder her stty "$s". Der Terminalstatus kümmert sich nicht um Subshells, daher bleiben diese Einstellungen unabhängig von der übergeordneten Shell erhalten. stty saneist nicht unbedingt das, was Sie tun möchten - es ist besser, den Zustand so wiederherzustellen, wie Sie ihn gefunden haben, als anzunehmen, dass sich der Zustand sanezu diesem Zeitpunkt befand. Wenn ich es nicht restaurieren echowürde, wären diese überall. Das herauszufinden ist teilweise der Grund, warum ich so spät gekommen bin - Ihre Antwort war nicht hier, als ich anfing zu testen.
Mikeserv
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.