当调用脚本退出时,使用 nohup 调用的脚本有时会死掉

当调用脚本退出时,使用 nohup 调用的脚本有时会死掉

我写了一个小 bash 脚本,显示了屏幕截图菜单 ( screenshot-menu.bash):

#!/usr/bin/env bash
echo 'Select an option:

s whole screen
w active window
r select region
'
read -rsn1 key
sleep 0.1

case $key in
  s|w|r) nohup ./screenshot.bash -$key & ;;
  *) echo Canceled. >> ./log.txt ;;
esac

该脚本nohup ./screenshot.bash使用参数运行。这是screenshot.bash

#!/usr/bin/env bash
while [[ "$#" > 0 ]]; do case $1 in
  -s|--screen) PARAM=''; TYPE='Screen'; shift;;
  -w|--window) PARAM='-u'; TYPE='Window'; shift;;
  -r|--region) PARAM='-s'; TYPE='Region'; shift;;
  *) echo "Unknown parameter passed: $1"; exit 1; shift; shift;;
esac; done

sleep 0.2
scrot $PARAM $(xdg-user-dir PICTURES)/'%Y-%m-%d %H.%M.%S $wx$h '$TYPE.png -e 'echo "$f" && notify-send -u low "$f"'

当我./screenshot-menu.bash在终端内运行,然后输入 时r,一切正常:我可以选择一个区域并获得该区域的屏幕截图。

但我希望能够从任何地方调用这个脚本。这就是为什么我有一个键绑定在新的终端窗口中运行此脚本:

alacritty -t "Screenshot menu" -o "window.dimensions.columns=20" -o "window.dimensions.lines=7" -e ~/bin/screenshot-menu.bash

(或更简单gnome-terminal -- ~/bin/screenshot.bash。)

这就是我使用nohup.我希望终端退出,因为我不想将其出现在屏幕截图中。但当然screenshot.bash不能死。

第二种方法(在新终端窗口中编写脚本)可能五次有效一次。其他时候,光标不会变成十字线来选择区域。我想,screenshot.bash虽然我用了,但还是死了nohup

是什么导致了这种随机行为?

答案1

问题很可能是在后台screenshot-menu.bash执行的进程 fork 收到 a并在运行之前退出。nohup ./screenshot.bash -$keySIGHUPnohup

一个简单的实验似乎证实了这一点:

$ alacritty -e sh -c 'nohup sleep 1000 &'
$ pgrep -a sleep
$

strace整个命令显示分叉,但在分叉进程收到一个(当终止并且它创建的伪终端消失时)并退出之前sh没有execve运行。nohupSIGHUPalacritty

在不寻找替代方法(在混合终端和图形应用程序时可能存在)的情况下,合理的解决方案是避免应用于nohup将进入后台的命令,而是在父进程上使用它。例如:

$ alacritty -e sh -c 'nohup sh -c "sleep 1000 &"'
$ pgrep -a sleep
625910 sleep 1000

screenshot-menu.bash可以因此改变这条线

s|w|r) nohup ./screenshot.bash -$key & ;;

进入

s|w|r) nohup sh -c "./screenshot.bash \"-$key\" &" ;;

或者,您可以在screenshot-menu.bashwith中启用作业控制set -m并删除nohup(然后 shell 将在其自己的进程组中启动后台管道(一个简单的命令也是管道),并且只有前台进程组在SIGHUP其控制终端时才会被传递)消失),前提是您可以确保没有任何内容screenshot.bash尝试从终端读取或写入终端(即您需要与 执行的操作等效的显式重定向nohup)。

例如,screenshot-menu.bash可能会变成:

#!/usr/bin/env bash

set -m

echo 'Select an option:
...
'

case $key in
  s|w|r) </dev/null >>./log.txt 2>&1 ./screenshot.bash "-$key" & ;;
  *) echo Canceled. >>./log.txt ;;
esac

也可以看看:

相关内容