从这个问题得出:如何获取shell脚本中最后执行的命令的pid?
那里的线程很好而且很清晰,但是如果命令实际上不需要末尾的“&”来后台运行怎么办?那么是否可以获得它的PID?
注意:我不想使用ps
and grep
;我问这个问题是因为理论上,在运行命令后,该进程的 PID 应该是可以找到的。
一个后台应用程序的示例 - 这不是我需要的,我需要一个通用的解决方案,用于任何自行进入后台的东西(有些强制它根本没有“前台”选项) - 但对于测试一个解决方案,这将工作得很好:
我的应用程序脚本:
#!/bin/bash
(
sleep 10
echo test
sleep 5
) &
正如您所看到的 '&' 位于脚本内部而不是外部,因此启动它不需要 &:
./my-app ; echo $! | od -c ; jobs | od -c
0000000 \n
0000001
0000000
$!为 null,并且jobs
不返回任何内容。 10 秒后,仍然会出现“测试”消息;还:
ps x | grep my-app | grep -v grep
14986 pts/5 S 0:00 /bin/bash ./my-app
更新,在一些评论说 bash 工作不适用于这里之后;我希望找到一个解决方案,而且我的希望不仅限于 bash :)
答案1
到背景本身,该应用程序分叉一个子进程并退出父进程。 shell 知道父进程,因为它是在执行命令本身之前分叉出来的进程,但它无法看到该进程可能生成的子进程或孙进程。
这种后台命令的一个更简单的示例是:
$ sh -c 'ps -j; sleep 10 &'
PID PGID SID TTY TIME CMD
6562 6562 14469 pts/13 00:00:00 sh
6563 6562 14469 pts/13 00:00:00 ps
14469 14469 14469 pts/13 00:00:00 zsh
我zsh
了解该sh
流程及其流程组。
然而,它看到的只是 6562 进程退出。它无法知道 6562 进程是否已生成 6563 进程。
$ ps -j
PID PGID SID TTY TIME CMD
6564 6562 14469 pts/13 00:00:00 sleep
6565 6565 14469 pts/13 00:00:00 ps
14469 14469 14469 pts/13 00:00:00 zsh
但是,您可以看到正在运行的进程sleep
也在该 6562 进程组中(不过,没有什么可以阻止命令启动新进程组,甚至会话(就像守护进程通常所做的那样))。
这些进程组仅在 shell 交互时才会创建。
您可以做的另一件事是:
cmd | cat &
wait
如果 cmd 生成的进程不关闭其标准输出,则cat
在它们全部死亡之前不会死亡。
答案2
这一般是不可能的。在您的具体情况下,这可能是可能的。
运行后台命令后,即可发现进程ID来自父母。如果您运行前台命令(主程序的子程序),并且该命令又运行后台命令(主程序的孙程序),则主程序对其孙程序没有直接可见性:它只知道它是运行一个子进程,该进程现已退出。
内核跟踪子进程→父进程的关系。可以运行ps -o ppid= -p $pid
查看ID为 的进程的父进程的进程ID $pid
。内核不跟踪进程的祖父母。此外,如果父进程死亡,该进程将被 init(进程号 1)采用,因此从那时起其父进程 ID 将为 1。
还有其他几个继承的进程属性,您可以尝试跟踪它们。然而,孙子可以脱离这些属性中的任何一个。如果孙子打算作为守护进程运行,它可能会尝试尽可能地隔离自己(无论是与中间子还是孙子本身),这样它就不会与您的交互式会话绑定,也不会冒着被卷入本次会议发生的任何事情的风险。
您可以在与当前进程相同的进程组中查找进程 ( ps -o pgid=
))。这将捕获在同一进程组中启动的任何其他进程,但相反,如果它由于调用子进程setpgid
或setpgrp
孙进程而在自己的进程组中运行,则会错过孙进程(守护进程会这样做)。
您可以在与当前进程相同的会话 ID 中查找进程 ( ps -o sid=
))。这将捕获在同一会话 ID 中启动的任何其他进程,但相反,如果它在自己的会话中运行,则由于调用setsid
子进程或孙进程(守护进程会这样做)而错过孙进程。
您可以打开一个临时文件并查找打开该文件的进程(`fuser "$tmpfile")。这更可靠,因为它只会捕获从打开此文件的主进程部分启动的进程,它不会捕获主进程的其他组件可能已执行的任何操作。然而,与其他解决方案一样,如果它或中间子进程关闭了它不使用的文件描述符,它将错过孙子进程,而守护进程会这样做。
大多数守护进程都有一个命令行选项可以留在前台。然后您可以运行daemon --foreground & daemon_pid=$!
,您需要采取预防措施以避免守护进程在会话退出时被捕获(nohup daemon --foreground </dev/null >daemon.log 2>&1 &
这是一个好的开始)。
答案3
您可以通过使用strace
脚本来获取一些信息,但它确实会保持 strace 运行,直到子命令完成。例如,
$ strace -b execve -e trace=none -e signal=none -f my-app
Process 21697 attached <-- is the () &
[pid 21696] +++ exited with 0 +++ <-- is my-app ending
Process 21698 attached <-- is the sleep 10
Process 21698 detached
test
Process 21702 attached <-- is the sleep 5
Process 21702 detached
+++ exited with 0 +++
所以我们有子 shell 的 pid,并且命令在那里运行,假设
echo
是一个内置 shell,因此不会跟踪它。
-b execve
是在执行时分离,-f
跟踪子级,-e
通过不跟踪任何内容来减少输出。