如何判断 Bash 中的 SIGWINCH 是否已激活

如何判断 Bash 中的 SIGWINCH 是否已激活

我试图让它在终端窗口大小发生变化时重新绘制一个框。我不确定如何捕捉这个信号,我在网上找到的文档并不是特别有帮助。

#!/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 1SCREEN(出现在之后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 更新变量的外部命令,无需查询。COLUMNScleartput

如果您希望脚本在陷阱之外执行某些操作,请将其放在循环之前(或循环之外)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 偶尔会发生,而不是一直发生,因此问题可以忽略不计。

相关内容