Wie kann ich einen Bash-Befehl weiterleiten und gleichzeitig dafür sorgen, dass Strg+C funktioniert?

Wie kann ich einen Bash-Befehl weiterleiten und gleichzeitig dafür sorgen, dass Strg+C funktioniert?

Erwägen Sie eine benutzerdefinierte Befehlszeilensoftware (z. B. loopHelloWorld), die Strg+C für ein reibungsloses Herunterfahren erkennt. Wie kann ich die Antwort weiterleiten, ohne sie zu verlieren Ctrl+C?

$ loopHelloWorld
    - Ctrl+C to nicely shutdown

Aber mit Pipe beendet die Pipe die Software ohne ordentliches Herunterfahren

$ loopHelloWorld | 
    while IFS= read -r line; do
        echo "$line"
    done

Beispiel

ping example.com |
  while IFS= read -r line; do
    echo "$line"
  done

Antwort1

Ctrl+Cbewirkt, dass ein SIGINT an alle Prozesse in der Pipeline gesendet wird (da sie alle in derselben Prozessgruppe ausgeführt werden, die diesem Vordergrundjob Ihrer interaktiven Shell entspricht).

Also rein:

loopHelloWorld | 
  while IFS= read -r line; do
    echo "$line"
  done

Sowohl der laufende Prozess loopHelloWorldals auch der Prozess, der die Subshell ausführt, die die whileSchleife ausführt, erhalten die SIGINT.

Wenn loopHelloWorlddie Ctrl+C to nicely shutdownNachricht auf die Standardausgabe geschrieben wird, wird sie auch in die Pipe geschrieben. Wenn dasnachdie Unterschale am anderen Ende ist bereits gestorben, dann loopHelloWorldwirdAuchErhalten Sie eine SIGPIPE, die Sie handhaben müssen.

Hier sollten Sie diese Meldung an stderr schreiben, da es sich nicht um die normale Ausgabe Ihres Befehls handelt (trifft pingjedoch nicht auf das Beispiel zu). Dann würde sie nicht durch die Pipe gehen.

loopHelloWorldOder Sie könnten die Subshell, die die While-Schleife ausführt, so einrichten, dass sie das SIGINT ignoriert, sodass sie die Ausgabe nach dem SIGINT weiterliest :

loopHelloWorld | (
  trap '' INT
  while IFS= read -r line; do
    printf '%s\n' "$line"
  done
)

Dies würde jedoch dazu führen, dass der Beendigungsstatus der Pipeline 0 ist, wenn Sie drücken Ctrl+C.

Eine weitere Option für dieses spezielle Beispiel wäre die Verwendung von zshoder ksh93anstelle von bash. In diesen Shells whilewürde die Schleife im Hauptprozess der Shell ausgeführt und wäre daher nicht von SIGINT betroffen.

loopHelloWorld | catDas würde allerdings nicht helfen, wenn die Prozessgruppe im Vordergrund ausgeführt wird cat.loopHelloWorld

Antwort2

Verwenden Sie eine Falle:

#! /bin/bash

trap handleInt SIGINT

interrupted=0

function handleInt {
    echo "Interrupted..."
    interrupted=1
}


while true
do
    [[ interrupted -ne 0 ]] && { echo "Ctrl-C caught, exiting..." ; exit ; }
    echo "Sleeping..."
    sleep 1
    read -r line
    echo "$line"
done

verwandte Informationen