Nohup

呼叫腳本退出時,使用 nohup 呼叫的腳本有時會死掉

  • April 3, 2021

我寫了一個小 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.

什麼可能導致這種隨機行為?

這個問題很可能是在後台screenshot-menu.bash執行的程序收到 a並在執行之前退出。nohup ./screenshot.bash -$key``SIGHUP``nohup

一個簡單的實驗似乎證實了這一點:

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

strace整個命令顯示分叉,但在分叉程序接收到一個- 當終止並且它創建的偽終端消失 - 並退出之前sh沒有execve執行。nohup``SIGHUP``alacritty

在不尋找替代方法(在混合終端和圖形應用程序時可能存在)的情況下,一個合乎邏輯的解決方案是避免應用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.bashset -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

也可以看看:

引用自:https://unix.stackexchange.com/questions/643234