在 bash 中安全退出 while 循环

Hen*_*son 8 scripting bash signals trap

假设我有一个 bash 脚本:

while :
do
    foo
done
Run Code Online (Sandbox Code Playgroud)

我希望能够从控制台运行此脚本并能够在任意时间退出它,只要它发生在两次 foo 运行之间。因此,如果说,我按Ctrl+ C(这可能是导致脚本退出的另一个操作,Ctrl+C只是一个示例),它将在执行 foo 后在下一个可用点退出:

while :
do
    foo
    if [pressed_ctrl_c]:
        break
done
Run Code Online (Sandbox Code Playgroud)

roa*_*ima 6

您可以尝试这种构造:

#!/bin/bash
#
INTR=
trap 'INTR=yes; echo "** INTR **" >&2' INT

while :
do
    (
        # Protect the subshell block
        trap '' INT

        # Protected code here
        echo -n "The date/time is: "
        sleep 2
        date
        read -t2 -p 'Continue (y/n)? ' YN || echo
        test n = "$YN" && echo "Asked for BREAK" >&2 && exit 90
    )
    SS=$?
    test 90 -eq $SS && echo "Matched BREAK" >&2 && break

    # Ctrl/C, perhaps?
    test yes = "$INTR" && echo "Matched INTR" >&2 && break
done
exit 0
Run Code Online (Sandbox Code Playgroud)

一些笔记

  • readtest一对演示交互式控制到内部受保护的代码段( ... )块。
  • exit 90是相当于break而是从一个子shell里。该test 0 != $? ...子shell块结束后立即行有没有捕捉exit 90状态和执行break的代码其实是想。
  • 子shell可以使用不同的退出状态值来表示不同类型所需要的控制流(breakexit,等...)
  • 这不会阻止程序安装自己的信号处理程序。例如,gdbSIGINT( CtrlC)安装自己的处理程序。如果目的是防止用户中断会话,更改中断键可能有助于混淆情况(请参阅下面的代码)。不优雅但可能有效。

更改终端上的 SIGINT 密钥

G=$(stty -g)                    # Save settings
test -n "$G" && stty intr ^A    # That is caret and A, not Ctrl/A

# ... SIGINT generated with Ctrl/A rather than Ctrl/C ...

test -n "$G" && stty "$G"       # Restore original settings
Run Code Online (Sandbox Code Playgroud)