如何获取子外壳的PID?


13

如何获取子外壳的PID?

例如:

$ echo $$
16808

这不起作用,因为原始外壳扩展了$$

$ ( echo $$ )
16808

为什么单引号不起作用?在原始外壳删除单引号之后,子外壳$$本身不会扩展吗?

$ ( echo '$$' )
$$

为什么也eval不起作用?是eval由subshel​​l运行的吗?为什么给我原始外壳的PID?

$ ( eval echo '$$' )
16808

谢谢。


我建议重新开放,因为我认为问题本质上是不同的(“如何避免$$扩展”与“子外壳中的不同pid”)。
peterh-恢复莫妮卡

Answers:


12

除了bash$BASHPID,你能够方便地用做:

pid=$(exec sh -c 'echo "$PPID"')

例:

(pid=$(exec sh -c 'echo "$PPID"'); echo "$$ $pid")

您可以将其变成一个函数:

# usage getpid [varname]
getpid(){
    pid=$(exec sh -c 'echo "$PPID"')
    test "$1" && eval "$1=\$pid"
}

请注意,某些外壳程序(例如zshksh93)不会为使用(...); 创建的每个子外壳程序启动子进程。在这种情况下,$pid可能最终与相同$$,这是正确的,因为这getpid是从中调用的进程的PID 。


1
不能。但是请不要假设子外壳程序必须在子进程中运行-例如,情况并非如此ksh93
mosvy

1
它将在ksh93中正常工作-它将始终返回从其调用的进程的pid。这是(...)来自示例的,它可能不会像那样生成单独的进程bash
mosvy

1
而且,某些外壳程序喜欢zshyash优化fork()子外壳程序中的最后一个命令。如果它是脚本中的最后一个命令,它们甚至可以优化子shell的fork,因此您getpid甚至可以报告的父项$$。您可以定义getpid为:getpid(){ sh -c 'echo "$PPID"'; return; }禁用以避免出现问题。
斯特凡Chazelas

1
@HaroldFischer 1.如果exec不进行优化或不进行优化,则该sh -c ...进程将是孙子进程,而不是$(...)使用命令替换的进程的子进程,$PPID并将成为$(...)子外壳程序的pid 。这就是上面的set -E+ trap ERRbash示例中发生的情况。
mosvy

1
@HaroldFischer 2. test "$1"测试是否$1为空字符串-一种快速而肮脏的方法,用于测试是否为该函数提供了varname将pid分配给其的参数;首先,使用功能并不是最聪明的主意
mosvy

18
$ echo $BASHPID
37152
$ ( echo $BASHPID )
18633

从手册中:

BASHPID

扩展为当前bash进程的进程ID。这与$$某些情况下有所不同,例如不需要重新初始化bash的子外壳。

$

扩展为外壳的进程ID。在()子外壳程序中,它扩展为当前外壳程序的进程ID,而不是子外壳程序。

有关:


谢谢。(1)“重新初始化”是什么意思?(2)您还可以考虑为什么我尝试过的那些方法行不通吗?
蒂姆(Tim)

@Tim我相信这是Gilles 在这里回答的。Bash根本不会$$在子Shell中更新。
库萨兰达

您是说在bash中无论如何我都应该始终使用$ BASHPID代替$$?我什么时候使用哪个?
蒂姆(Tim)

@Tim取决于您是否在子Shell中想要获取脚本或子Shell的进程ID。提供了两种可能性,正确的一种取决于应用程序。无法对此给出更具体的答案。
库萨兰达

1
@Tim无法可靠地找到子外壳的父外壳的PID,除非您安排保存$BASHPID在变量中并在子外壳中使用它。有$PPID,但从某种意义上说,它$$是外壳程序的父PID,与外壳程序的PID 含义相同(它不会在子外壳程序中重置)。没有$BASHPPID变量。
库萨兰达
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.