【问题标题】:Get pid of current subshell获取当前子shell的pid
【发布时间】:2014-01-10 15:10:25
【问题描述】:

我正在尝试获取当前正在执行的子 shell 的 pid - 但 $$ 只返回父 pid:

#!/usr/bin/sh

x() {
  echo "I am a subshell x echo 1 and my pid is $$"
}

y() {
  echo "I am a subshell y echo 1 and my pid is $$"
}


echo "I am the parent shell and my pid is $$"
x &
echo "Just launched x and the pid is $! "

y &
echo "Just launched y and the pid is $! "

wait

输出

I am the parent shell and my pid is 3107
Just launched x and the pid is 3108
I am a subshell x echo 1 and my pid is 3107
Just launched y and the pid is 3109
I am a subshell y echo 1 and my pid is 3107

正如您在上面看到的,当我从后台运行的函数中运行 $$ 时,它不会像从父 shell 中运行 $! 时那样显示 PID。

【问题讨论】:

标签: bash sh


【解决方案1】:

最好使用$(exec sh -c 'echo $PPID') 而不是 John1024 在回答中显示的技巧(当我写回复时,这些技巧目前不适用于现代 shell)。

看到这个问题: https://unix.stackexchange.com/questions/484442/how-can-i-get-the-pid-of-a-subshell

【讨论】:

  • 这看起来与我的答案基本相同,并且在我的测试中,它给出了相同的结果。使用什么“现代外壳”(名称和版本)会得到不同的结果?
【解决方案2】:

如果你使用 Linux-kernel,你可以使用 Linux-kernel 的/proc/self 功能来做到这一点:

最简单的形式:cd -P /proc/self && basename "${PWD}"

保留PWD 和OLDPWD 变量:PWD_BACKUP="${PWD}";OLDPWD_BACKUP="${OLDPWD}";cd -P /proc/self && basename "${PWD}";cd "${PWD_BACKUP}";OLDPWD="${OLDPWD_BACKUP}"

例如:

$ cd -P /proc/self && basename "${PWD}"
26758
$ (cd -P /proc/self && basename "${PWD}")
26959
$ (cd -P /proc/self && basename "${PWD}")
26961
$ 

【讨论】:

    【解决方案3】:

    @John1024 的回答很好,但是里面有个小问题。

    当一个命令像(...)一样运行时,该命令将在一个新的子进程中运行,所以

    ( : ; bash -c 'echo $PPID' ) 
    

    将返回(...)的process_id,而不是调用(...)的函数的进程id

    如果你想要函数的 process_id,你可以运行:

    $SHELL -c 'echo $PPID'
    

    并且process_id会输出到stderr

    如果你想从变量中获取函数的 process_id,你可以运行:

    $SHELL -c 'echo $PPID' | read -s func_pid
    

    然后你可以从变量 ${func_pid} 中获取 pid

    注意:不要在(...)中运行该命令,否则会返回(...)的process_id

    【讨论】:

    • (1)我不明白你要表达的意思。问题是如何找到当前subshel​​l的PID。 我猜这就是你所说的“(...)的process_id b>”——因此,您似乎在抱怨 John1024 的回答符合问题的要求。 (2)顺便说一句,这个问题的整个前提是echo $$会告诉你当前主shell进程的PID;所以我看不出有任何需要或使用外部$SHELL -c 'echo $PPID'的括号。 …(续)
    • (续)...  (3)您的最终产品,$SHELL -c 'echo $PPID' | read -s func_pid;不起作用(至少不是 inbash;cmd | read var 通常不起作用)。
    • 投了反对票,这个答案似乎太奇怪了。首先,我们需要 subshel​​l 的 PID,因为这里的问题是获取在(...) 内运行的 shell 的 PID,而不是在(...) 之外运行的 shell 的 PID。同样mypid=wrong; $SHELL -c 'echo $PPID' | read -s mypid; echo $mypid 输出wrong 作为管道在子shell 中运行(至少在大多数shell 中,如bash)。使用mypid=$(exec sh -c 'echo $PPID') 代替:( mypid=$(exec sh -c 'echo $PPID'); pstree -p | grep --color=always $mypid )。如果您使用反引号而不是 $(..),这也非常便携
    【解决方案4】:

    现代 bash

    如果您运行的是 bash v4 或更高版本,则子 shell 的 PID 在$BASHPID 中可用。例如:

    $ echo $$ $BASHPID ; ( echo $$ $BASHPID  )
    32326 32326
    32326 1519
    

    在主 shell 中,$BASHPID 与 $$ 相同。在子shell中,更新为子shell的PID。

    旧 bash(版本 3.x 或更早版本)

    Pre version 4,你需要一个workaround:

    $ echo $$; ( : ; bash -c 'echo $PPID' )
    11364
    30279
    

    (帽子提示:kubanczyk)

    为什么是冒号?

    请注意,如果没有冒号,解决方法不起作用:

    $ echo $$; ( bash -c 'echo $PPID' )
    11364
    11364
    

    看来,在上面,子shell 从未创建,因此第二个语句返回主shell 的PID。相比之下,如果我们在括号内放置两个语句,则会创建子 shell,并且输出与我们预期的一样。即使另一个语句只是一个冒号:,也是如此。在 shell 中,: 是无操作的:它什么也不做。然而,在我们的例子中,它会强制创建足以完成我们想要的子shell。

    破折号

    在类似 debian 的系统上,dash 是默认的 shell (/bin/sh)。 PPID 方法适用于 dash,但还有另一个变化:

    $ echo $$; (  dash -c 'echo $PPID' ) 
    5791
    5791
    $ echo $$; ( : ; dash -c 'echo $PPID' )
    5791
    5791
    $ echo $$; (  dash -c 'echo $PPID'; : )   
    5791
    20961
    

    对于dash,将: 命令放在命令之前是不够的,但将其放在命令之后。

    POSIX

    PPID 包含在POSIX specification 中。

    便携性

    mklement0 报告说,bash、dash、和 zsh 但不是 ksh:

    echo $$; (sh -c 'echo $PPID' && :)
    

    【讨论】:

    • bash -c 'echo $PPID'有多复杂
    • @kubanczyk 感谢您的建议。你是对的,这是一个很好的解决方案。我将它添加到答案中,并解释了为什么 echo $$; ( bash -c 'echo $PPID' ) 不能像我们希望的那样工作。
    • 干得好;事实证明,将: 放在 dash -c 之后就足够了(无需调用外部实用程序ls)。鉴于$$PID 是POSIX 强制变量,因此以下命令按原样 与dash、bash 和zsh 一起工作(但奇怪的是,不是 与ksh): echo $$; (sh -c 'echo $PPID' && :)
    • @mklement0 有趣!答案已更新。虽然我可以接受 ksh 对 PPID 的特殊解释,但我发现 dash 的行为确实令人费解。
    • 谢谢;回复dash:我自己也很困惑。请注意,它不是 ksh 解释$PPID,但是:它是sh(通常不是ksh);在没有深入研究的情况下,ksh 似乎在其他 shell 确实使用它们的许多情况下避免了子 shell。
    【解决方案5】:

    输出是正确的。

    这是来自 bash 的手册页。

    特殊参数

    shell 专门处理几个参数。这些参数只能被引用;不允许分配给他们。

    * 扩展为位置参数,从一开始。当扩展发生在双引号内时,它会扩展为单个单词,每个单词的值 由 IFS 特殊变量的第一个字符分隔的参数。也就是说,"$*" 等价于 "$1c$2c...",其中 c 是 IFS 变量的值。如果未设置 IFS,则参数以空格分隔。如果 IFS 为空,则连接参数而不插入分隔符。
    @ 扩展为位置参数,从 1 开始。当扩展出现在双引号内时,每个参数都扩展为一个单独的单词。那 也就是说,"$@" 等价于"$1" "$2" ... 如果双引号展开出现在一个单词中,则第一个参数的展开与原词的开头部分连接,最后一个参数的展开与原词的最后一部分。当没有位置 参数,"$@" 和 $@ 扩展为空(即它们被删除)。
    # 扩展为十进制的位置参数个数。
    ? 扩展为最退出状态最近执行的前台管道。
    - 扩展为调用时指定的当前选项标志,由 set 内置命令,或由 shell 本身设置的那些(例如 -i 选项)。
    @987654333 @ 扩展为 shell 的进程 ID。在() 子shell 中,它扩展为当前shell 的进程ID,而不是子shell。
    ! 扩展为最近执行的后台(异步)命令的进程ID。

    要在子外壳中获取 PID,您可以使用 BASHPID。这是一个仅限 bash 的环境变量。

    您的新脚本将如下所示。

    #!/bin/bash
    
    x() {
      echo "I am a subshell x echo 1 and my pid is $BASHPID"
    }
    
    y() {
      eval echo "I am a subshell y echo 1 and my pid is $BASHPID"
    }
    
    
    echo "I am the parent shell and my pid is $$"
    x &
    echo "Just launched x and the pid is $! "
    
    y &
    echo "Just launched y and the pid is $! "
    
    wait
    

    【讨论】:

    • 我不怀疑它是正确的 - 我目前的问题是有没有办法从那个子外壳内部获取子外壳的 PID?
    • 我更新了我的回复。您可以使用 BASHPID 在 bash 脚本中获取子 shell 的 PID。
    【解决方案6】:

    环境:

    SUSE Linux Enterprise Server 10 SP2 (i586)

    GNU bash,版本 3.1.17(1)-release (i586-suse-linux) 版权所有 (C) 2005 Free Software Foundation, Inc.

    #!/usr/bin/sh
    
    x() {
      mypid=$(awk 'BEGIN {print PROCINFO["ppid"] ; exit}')
      echo "I am a subshell x echo 1 and my pid is $mypid"
    }
    
    y() {
      mypid=$(awk 'BEGIN {print PROCINFO["ppid"] ; exit}')
      echo "I am a subshell y echo 1 and my pid is $mypid"
    }
    
    
    echo "I am the parent shell and my pid is $$"
    x &
    echo "Just launched x and the pid is $! "
    
    y &
    echo "Just launched y and the pid is $! "
    
    wait
    

    结果:

    I am the parent shell and my pid is 27645
    Just launched x and the pid is 27646
    Just launched y and the pid is 27647
    I am a subshell y echo 1 and my pid is 27647
    I am a subshell x echo 1 and my pid is 27646
    

    【讨论】:

    • 可以用其他命令代替awk,例如:sh -c 'echo $PPID'
    猜你喜欢
    • 1970-01-01
    • 2013-12-22
    • 2010-09-22
    • 1970-01-01
    • 2023-04-06
    • 2012-03-19
    • 1970-01-01
    • 2014-10-05
    • 2015-03-31
    相关资源
    最近更新 更多