我试图让它在终端窗口大小发生变化时重新绘制一个框。我不确定如何捕捉这个信号,我在网上找到的文档并不是特别有帮助。
#!/bin/bash
SCREEN(){
line=$(tput lines)
col=$(tput cols)
clear
for (( i = 1; i <= $line; i++ )); do
for (( j = 1; j <= $col; j++ )); do
if (( 1 == i || $line == i || 1 == j || $col == j )); then
echo -n "*"
else
echo -n " "
fi
done
echo
done
}
SCREEN;
trap SCREEN SIGWINCH
while true; do
if [[ SIGWINCH == "true" ]]; then
sleep 1
SCREEN
fi
done
我的理解是,需要执行此陷阱来获取终端大小改变的事件。如何才能使 SIGWINCH 发生时逻辑发生?
答案1
分析
tmux
对我来说,您的脚本可以正常工作konsole
(我只需要从echo
函数中删除最后一个,以使整个框保持可见)。但是,脚本的工作方式(我认为)并不像您想象的那样。
与其中一条评论所说的相反,trap SCREEN SIGWINCH
这是一条很好的命令。便携应该是trap SCREEN WINCH
,但是trap
在 Bash 中 SIGWINCH
和 一样好WINCH
。
[[ SIGWINCH == "true" ]]
返回退出状态 1(失败),因为字符串SIGWINCH
不等于字符串true
。实际上sleep 1
和SCREEN
(出现在之后then
)永远不会运行。您的while
循环只会true
一次又一次地调用和比较两个不同的固定字符串。
当 SIGWINCH 到来时,陷阱就会起作用并SCREEN
执行。在我的测试中,脚本会对其“窗口”(在我的情况下是 tmux 窗格)大小的变化做出反应。
你的while
循环本身不执行任何操作,但如果不是循环,脚本将在徒劳设置陷阱后退出。
改进
这是一个修改后的脚本,它实现了我认为您想要的逻辑:
#!/bin/bash
SCREEN () {
clear
for (( i = 1; i <= "$LINES"; i++ )); do
for (( j = 1; j <= "$COLUMNS"; j++ )); do
if (( 1 == i || "$LINES" == i || 1 == j || "$COLUMNS" == j )); then
echo -n "*"
else
echo -n " "
fi
done
done
}
trap SCREEN WINCH
SCREEN # to draw the first box
while true; do
sleep 1
done
注意我删除了您对 的调用tput
。默认情况下,bash在每个外部(非内置)命令后设置LINES
和。在我们的例子中, 是使 shell 更新变量的外部命令,无需查询。COLUMNS
clear
tput
如果您希望脚本在陷阱之外执行某些操作,请将其放在循环之前(或循环之外)while
。请记住,当 shell 等待同步命令退出时,陷阱(一般来说:陷阱)无法运行。在我们的例子中,陷阱可以在之后true
或之后运行sleep 1
,但不能在期间运行;这就是为什么脚本并不总是立即重新绘制框的原因,通常会有延迟,因为 shell 等待sleep 1
完成。如果引入长时间运行的同步命令,那么它同样会使脚本停止更新框,直到命令完成。一种解决方案是异步运行命令并wait
为其运行。关键是信号可以中断wait
。
以下示例脚本将几乎立即对其窗口大小的变化做出反应:
#!/bin/bash
SCREEN () {
clear
for (( i = 1; i <= "$LINES"; i++ )); do
for (( j = 1; j <= "$COLUMNS"; j++ )); do
if (( 1 == i || "$LINES" == i || 1 == j || "$COLUMNS" == j )); then
echo -n "*"
else
echo -n " "
fi
done
done
}
trap SCREEN WINCH
SCREEN # to draw the first box
while true; do
sleep 10 &
wait
done
我可以tail -f /dev/null &
在循环之前使用(此命令永远不会自动结束) 和wait
循环内部,但在某些情况下tail
可能会在脚本终止后继续存在,我们不想让这种情况发生。sleep 10
必须在循环中不断更新,但每个实例最终都会退出,无论如何。另一方面,中断的 SIGWINCHwait
不会杀死任何后台sleep
,因此如果信号频繁出现,则后台sleep
进程的数量将增加到大约“每 10 秒的信号数”的值。实际上,SIGWINCH 偶尔会发生,而不是一直发生,因此问题可以忽略不计。