看下面这个答案对于 zsh 解决方案
假设~/.bash_aliases
在暂停系统之前我需要确认以下内容:
function suspend()
{ #
echo "Please confirm/cancel system suspension:"
select confirmation in "confirm" "cancel"; do
case ${confirmation} in
confirm )
echo "System suspending..."
systemctl suspend
break;;
cancel )
echo "Canceled suspension."
break;;
esac
done
}
systemctl suspend
如果用户没有给出答案,我希望仍然被执行。例如,10秒后没有用户输入,则执行“确认”案例的内容。
sleep
我在子 shell 中尝试了以下操作:
function suspend()
{ #
flag_cancel=0
echo "Please confirm/cancel system suspension:"
(
sleep 10 &&
if [ $flag_cancel -eq 0 ]; then
echo "System suspending..."
systemctl suspend
fi &
)
select confirmation in "confirm" "cancel"; do
case ${confirmation} in
confirm )
echo "System suspending..."
systemctl suspend
break;;
cancel )
flag_cancel=1
echo "Canceled suspension."
break;;
esac
done
}
flag_cancel
但不考虑值的变化,因此该命令始终在sleep
.
如何实现我想要的?
答案1
尝试使用read
命令在运行时获取用户的输入。因为它有超时选项。
来自男人:
-t timeout 如果在超时秒内未读取完整的输入行,则导致读取超时并返回失败。 timeout 可以是小数点后带有小数部分的十进制数。仅当 read 正在从终端、管道或其他特殊文件读取输入时,此选项才有效;从常规文件读取时它没有效果。如果超时为 0,如果指定文件描述符上的输入可用,则 read 返回成功,否则返回失败。如果超过超时,则退出状态大于 128。
答案2
您的尝试sleep
将不起作用,因为调用sleep
和后续测试$flag_cancel
发生在后台作业中。对代码主要部分中变量的任何更改flag_cancel
都不会影响后台子 shell 中变量的值,并且代码将在 10 秒后无条件挂起系统。
read
相反,您可以使用和在 中几秒select
后超时的事实。$TMOUT
bash
这是第一段代码的主题的变体:
suspend_maybe ()
{
local PS3='Please confirm/cancel system suspension: '
local TMOUT=10
local do_suspend=true
select confirmation in confirm cancel; do
case $REPLY in
1)
# default case
break ;;
2)
do_suspend=false
break ;;
*)
echo 'Sorry, try again' >&2
esac
done
if "$do_suspend"; then
echo 'Suspending...'
systemctl suspend
else
echo 'Will not suspend'
fi
}
所做的更改:
- 现在调用该函数是
suspend_maybe
因为.suspend
bash
- 循环
select
用于PS3
其提示。 - 循环在几秒钟
select
后超时$TMOUT
。 - 我们在声明中使用数字
case
。这样我们就不必将所有字符串输入两次。的值$REPLY
将是用户输入的任何值。 - 我们只需要
select
循环来告诉我们用户是否想要取消系统暂停。我们将暂停视为默认操作。 - 一旦退出循环
select
,我们就会暂停系统,除非用户选择取消该操作。
相同的事情,但使用输入循环read
作为以下内容的直接替代select
:
suspend_maybe ()
{
local PS3='Confirm system suspension [y]/n: '
local TMOUT=10
local do_suspend=true
while true; do
if ! read -p "$PS3"; then
# timeout
break
fi
case $REPLY in
[yY]*)
# default case
break ;;
[nN]*)
do_suspend=false
break ;;
*)
echo 'Sorry, try again' >&2
esac
done
if "$do_suspend"; then
echo 'Suspending...'
systemctl suspend
else
echo 'Will not suspend'
fi
}
这里,用户可以输入任何以n
或开头的字符串N
来取消暂停。超时、输入以或read
开头的单词或按都会暂停系统。y
Y
Ctrl+D
使用上面的循环,可以更轻松地捕获超时情况,以防您在由于用户未响应提示而发生暂停时想要执行任何特殊操作。每当调用失败时(或者在输入流关闭时),都会触发 - 语句break
中的。if
read
答案3
一般来说,您可以选择的替代方案timeout
是GNU 核心工具。这不是最好的选择,因为我们正在为内置命令设置超时,而外部实用程序只能通过生成整个 shell 来完成此操作:
if ! timeout 10 bash -c '
select conf in yes no
do
case $conf in
(yes) exit 1;;
(no) exit 0;;
esac
done'
then
echo 'Suspending'
fi
如果您要通过某些外部工具(可能是图形工具)请求用户输入,可能会更有趣(尽管其中有几个工具实现了自己的超时机制)。
请注意,当不直接从 shell 提示符调用时timeout
需要该--foreground
选项(例如,如果您在 subshell: 中调用它( timeout ... )
)并且需要从终端读取。
答案4
你也可以用 来做到这一点at
。优点:如果您必须学习一种新工具,请学习最通用的工具。at
在未来的任意时间安排命令。可以使用 来从队列中删除计划的作业atrm
。
在你的情况下:
function suspend()
{ #
JOBNO=$(echo "systemctl suspend" | at now + 0.2 seconds 2>&1 > /dev/null | awk '{print $2}')
echo "Please confirm/cancel system suspension:"
select confirmation in "confirm" "cancel"; do
case ${confirmation} in
confirm )
echo "System suspending..."
systemctl suspend
break;;
cancel )
echo "Canceled suspension."
break;;
esac
atrm $JOBNO
done
}