Bash 中使用条件执行来根据子进程的退出状态控制脚本的流程。然而,在某些情况下,子进程(应用程序)在后台启动,用于交互目的,因此没有退出,也没有退出状态,直到它关闭。
考虑一些例子,比如启动 pdf 查看器,多普夫夫。考虑第一种情况,其中存在一个文件 example.pdf:
$ mupdf example.pdf &
[1] 16220
这可行,所以让我们尝试条件执行:
$ mupdf example.pdf & && echo TRUE
bash: syntax error near unexpected token '&&'
嗯。好吧,让我们尝试另一种(人为的)方法,试图避免错误:
$ if mupdf example.pdf &; then echo TRUE; fi
bash: syntax error near unexpected token ';'
怎么样:
$ if (mupdf example.pdf &); then echo TRUE; fi
TRUE
这看起来很有希望,但是我们尝试第二种情况,使用一个不存在的文件:
$ mupdf nofile.pdf &
[1] 16282
$ error: cannot open nofile.pdf
error: cannot load document 'nofile.pdf'
mupdf: error: cannot open document
$ if (mupdf nofile.pdf &); then echo TRUE; fi
TRUE
$ error: cannot open nofile.pdf
error: cannot load document 'nofile.pdf'
mupdf: error: cannot open document
该构造也不起作用。因此,通常的条件执行方法不起作用,因为没有退出状态。但我注意到,在成功和不成功的情况下,最初都会创建一个 pid(多普夫夫在第二种情况下无法打开 - 启动最终失败)。
所以尝试另一种方法,等待看看后台pid会发生什么,所以我们编写了以下脚本:
#!/usr/bin/env bash
# usage: ./show <file>.pdf
mupdf "$1" &>/dev/null &
sleep 2
if kill -0 $! &>/dev/null
then
echo TRUE
else
echo FALSE
fi
# end file
并测试第一种情况:
$ ./show example.pdf # case 1
[1] 16602
TRUE
没关系。现在来说第二种情况:
$ ./show nofile.pdf # case 2
[2] 16699
[2]+ Exit 1 mupdf nofile.pdf &> /dev/null
FALSE
嗯,这也有效。但这似乎是一种非常复杂的检查进程的方法,如果启动需要更多时间(超过 2 秒),则可能会失败。所以我的问题是,有没有更好(更干净)的方法来解决这个问题?
答案1
我找到了许多与此主题类似的主题,考虑使用以下内容xdo工具和控制面板用于测试持久窗口应用程序或其他持久子进程的启动。但使用这些似乎是不必要的复杂化。下面的解决方案将允许有条件地执行持久子流程。它利用半秒的睡眠持续时间。如果感兴趣的子进程需要更长的时间才能成功启动,则需要相应增加此持续时间。
bash 源文件发射
#/usr/bin/env bash
# launch : will allow launching a windowed application or persistent
# sub-process in the background so that conditional execution can be
# applied to determine whether or not it has launched successfully -
# dependencies: GNU coreutils (sleep), util-Linux (kill)
# usage: ./launch process arg_1 .. arg_n && echo TRUE || echo FALSE
$@ &>/dev/null & sleep 0.5 && kill -0 $! 2>/dev/null
# end file
以便:
$ ./launch mupdf example.pdf && echo TRUE || echo FALSE
TRUE
$ ./launch mupdf nofile.pdf && echo TRUE || echo FALSE
FALSE
作为此信息的后记,我认为包含以下建议会很有用:麦克塞夫在对开篇文章的评论中。
鉴于上述构造完成了当前任务,麦克塞夫指出它可能会导致其他问题,因为它不会将信号传播到调用脚本;相反,它会忽略并丢弃它。这是一个非常重要的观察。
信号是进程间通信的关键元素,并且可以导致事件驱动脚本的可能性。因此,为了获得更健壮和有效的脚本,麦克塞夫建议采用以下方法来捕获信号,以便可以传播信号:
{ mupdf some.pdf || kill -"$(($?&127))" "$$"; } &
其中$?&127
允许操作 1 到 128 个可能的信号 (POSIX),我的 Debian 系统有 64 个,因此信号掩码可以$?&63
代替。
因此,作为结束语,对于非常简单的脚本,进程间通信不感兴趣,建议发射可能很有用,但对于更多的事情,特别是在进程间通信很重要的情况下,捕获(并传播)信号,如建议的麦克塞夫。
答案2
作为单行:
mupdf example.pdf & echo mupdf is now in the background; wait $! && echo FINISHED successfully