【问题标题】:Support for Ctrl+C / Ctrl+Z at " answer=$( while ! head -c 1 | grep -i '[ny]' ; do true ; done ) "在“ answer=$( while !head -c 1 | grep -i '[ny]' ; do true ; done ) ”处支持 Ctrl+C / Ctrl+Z
【发布时间】:2019-10-11 02:47:54
【问题描述】:

为了获得用户的[Y/N] 决定,我使用了一个“yesno”函数,其中包含一个循环:

answer=$( while ! head -c 1 | grep -i '[ny]' ; do true ; done )

但是,这个循环阻塞了一个控制台,并且无法通过按 Ctrl + C Ctrl + Z kbd>。添加类似[\x03](ascii for Ctrl + C )或[\x1A](ascii for Ctrl + Z ) 也无济于事。如何在不依赖其他工具的情况下添加此功能,同时保持 POSIX 兼容性?

#!/bin/sh

yesno () {
    printf "$1 [Y/N] "
    old_stty_cfg=$( stty -g )
    stty raw -echo
    answer=$( while ! head -c 1 | grep -i '[ny]' ; do true ; done )
    stty "$old_stty_cfg"
    if printf "$answer" | grep -iq "^y" ; then
        return 0
    else
        return 1
    fi
}

if yesno "Question?" ; then
    printf "yes\n"
    return 0
else
    printf "no\n"
    return 1
fi

【问题讨论】:

  • 我不确定我是否理解,你为什么不用read
  • @BenjaminW。这是较大脚本的一部分,它没有在任何地方使用read / awk,我不知道它们的某些实现是否在某些系统上的工作方式不同 - 所以我希望添加 Ctrl+C / Ctrl+Z 依赖已经使用过的head / grep / printf
  • read 是一个特殊的内置函数,因此您无需担心每个操作系统的实现,只需担心 POSIX 是否保证 shell 将提供您需要的最少功能。跨度>

标签: bash shell grep sh posix


【解决方案1】:

对此解决方案及其 POSIX 兼容性的评论 - 将不胜感激!

#!/bin/sh
enter=$( printf '\015' )
ctrl_c=$( printf '\003' )
ctrl_x=$( printf '\030' )
ctrl_z=$( printf '\032' )

yesno () {
    printf '%b [Y/N] ' "$1"
    old_stty_cfg=$( stty -g )
    stty raw -echo
    while true ; do
        answer=$( head -c 1 )
        case $answer in *"$ctrl_c"*|"$ctrl_x"*|"$ctrl_z"*)
            stty "$old_stty_cfg"
            exit 1
            ;;
            *"y"*|"Y"*)
            stty "$old_stty_cfg"
            return 0
            ;;
            *"n"*|"N"*)
            stty "$old_stty_cfg"
            return 1
            ;;
        esac
    done
}

if yesno "Question?" ; then
    printf "yes\n"
    exit 0
else
    printf "no\n"
    exit 1
fi

【讨论】:

  • $'' 本身不是 POSIX 兼容的语法,仅适用于扩展的 shell。
  • 另外,case $answer in [Yy]*) 比使用grep 的管道效率
  • 我建议,当您的脚本第一次启动时,将您想要的值分配给您以后可以再次引用的变量。 ctrl_c=$(printf '\003'); ctrl_v=$(printf '\030'); ctrl_z=$(printf '\032')。然后,您可以使用case $answer in *"$ctrl_c"*|*"$ctrl_v"*|*"$ctrl_z"*) ...; 轻松快速地与这些值进行比较。
  • ...分配效率低/成本高,因为它正在启动一个子shell 来运行printf,但由于您只执行一次,它会被计入启动成本。
  • 顺便说一句,一般来说,printf "$1 [Y/N] " 是错误的形式;考虑printf '%s [Y/N] ' "$1",因此您的字符串内容不能被视为格式字符串(或%b 而不是%s,如果您想要这可以用于反斜杠转义序列)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-01-08
  • 1970-01-01
  • 2014-01-05
  • 2021-03-30
  • 1970-01-01
  • 2014-11-04
相关资源
最近更新 更多