如何获得刚刚开始的过程的pid


70

我想启动进程(例如myCommand)并获取其pid(以便稍后将其杀死)。

我尝试了ps并按名称过滤,但无法按名称区分进程

myCommand
ps ux | awk '/<myCommand>/ {print $2}' 

因为进程名称不是唯一的。

我可以通过以下方式运行流程:

myCommand &

我发现可以通过以下方式获取此PID:

echo $!

有没有更简单的解决方案?

我很乐意执行myCommand并通过一行命令获得其PID。

Answers:


77

有什么比这更简单的echo $!呢?作为一行:

myCommand & echo $!

谢谢您将这些命令与“&”合并对我有很大帮助。
rafalmag

8
在bash脚本中,在启动程序的循环中,$!是不准确的。有时它返回脚本本身的pid,有时从脚本中返回grep或awk运行的任何解决方案,以专门获取在这种情况下刚启动的进程的pid?像pid = myprogram这样的东西真是太棒了

2
注意,这要求您使用a &作为上一行开始命令,否则echo基本上返回空白。
rogerdpack

2
像这样分配变量将command & echo $!冻结该步骤的执行:(
Shashank Vivek

26

用小脚本包装命令

#!/bin/bash
yourcommand &
echo $! >/path/to/pid.file

20

您甚至可以在命令运行之前使用sh -cexec获取命令的PID 。

要启动myCommand,以便在其开始运行之前先打印其PID,可以使用:

sh -c 'echo $$; exec myCommand'

这个怎么运作:

这将启动一个新的外壳程序,打印该外壳程序的PID,然后使用exec内置命令用命令替换该外壳程序,以确保其具有相同的PID。当您的shell使用exec内置命令运行命令时,您的shell 实际上正在成为该命令,而不是派生自己的新副本的更为常见的行为,该副本具有自己独立的PID,然后成为命令。

我发现,这比涉及异步执行(带有&),作业控制或使用搜索的替代方案要简单得多ps。这些方法很好,但是除非您有特定的理由使用它们(例如,也许该命令已经在运行,在这种情况下搜索其PID或使用作业控制才有意义),我建议您首先考虑采用这种方法。(而且我当然不会考虑编写复杂的脚本或其他程序来实现此目的)。

此答案包括此技术的示例。


该命令的某些部分有时会被省略,但通常不会被省略。

即使您正在使用的Shell是Bourne样式,因此支持exec具有这些语义的内建函数,您通常也不应为此避免使用sh -c(或等效方法)创建一个新的单独的 Shell进程,因为:

  • 一旦shell变为myCommand,就没有shell等待运行后续命令了。用替换自身后将sh -c 'echo $$; exec myCommand; foo无法尝试运行。除非您编写的脚本是最后一个运行该脚本的脚本,否则不能只在运行其他命令的shell中使用它。foomyCommandecho $$; exec myCommand
  • 您不能为此使用子外壳(echo $$; exec myCommand)语法上可能比更好sh -c 'echo $$; exec myCommand',但是当您在$$内部运行时( ),它给出的是父外壳的PID,而不是子外壳本身的PID。但是,子外壳的PID将成为新命令的PID。一些外壳程序提供了自己的非便携式机制来查找子外壳程序的PID,您可以将其用于此目的。特别是在Bash 4中(echo $BASHPID; exec myCommand)确实可以工作。

最后,请注意,某些外壳程序将执行优化,在此情况下,当exec它们知道以后不需要执行任何操作时,它们将运行命令,好像是通过(即,它们首先放弃了)。一些shell会尝试在它是最后一个要运行的命令时执行此操作,而其他shell只会在该命令之前或之后没有其他命令时执行此操作,而其他shell则根本不会执行。这样做的结果是,如果您忘记编写exec而只是使用sh -c 'echo $$; myCommand'它,那么在某些具有某些 shell的系统上,有时它会为您提供正确的PID 。我建议不要过分依赖这种行为,而应始终包括您所需要的时间。exec


在运行之前myCommand,需要在bash脚本中设置许多环境变量。这些会继续执行exec命令的环境吗?
user5359531

看起来我的环境确实继承了exec命令。但是,这种方法在myCommand启动其他流程(您需要使用这些流程)时不起作用。当我发出以这种方式获得的kill -INT <pid>位置pid时,信号不会到达由开始的子进程myCommand,而如果我myCommand 在当前会话中运行并按Ctrl + C,则信号会正确传播。
user5359531

1
我试过了,但是myCommand进程的pid似乎是echo $$ +1的pid输出。难道我做错了什么?
crobar

我的命令如下所示:sh -c 'echo $$; exec /usr/local/bin/mbdyn -f "input.file" -o "/path/to/outputdir" > "command_output.txt" 2>&1 &'
crobar

7

我不知道任何更简单的解决方案,但是没有使用$!够好了?正如其他人所说,如果以后需要,您可以始终将值分配给其他变量。

另外,您可以使用pgrep或代替ps的管道pidof


5

将pid注册到文件后,从bash脚本使用exec:

例:

假设您有一个要使用args p1,p2,p3运行的名为“ forever.sh”的脚本

forever.sh源代码:

#!/bin/sh

while [ 1 -lt 2 ] ; do
    logger "$0 running with parameters \"$@\""
    sleep 5
done

创建一个reaper.sh:

#!/bin/sh

echo $$ > /var/run/$1.pid
exec "$@"

通过reaper.sh运行forever.sh:

./reaper.sh ./forever.sh p1 p2 p3 p4 &

forever.sh只不过每5秒记录一行到syslog

您现在在/var/run/forever.sh.pid中具有pid

cat /var/run/forever.sh.pid 
5780

并且forever.sh运行正常。syslog grep:

Nov 24 16:07:17 pinkpony cia: ./forever.sh running with parameters "p1 p2 p3 p4"

您可以在过程表中看到它:

ps axuwww|grep 'forever.sh p1' |grep -v grep
root      5780  0.0  0.0   4148   624 pts/7    S    16:07   0:00 /bin/sh ./forever.sh p1 p2 p3 p4

3
哦,还有“ oneliner”:/ bin / sh -c'echo $$> / tmp / my.pid && exec program args'&
user237419 2010年

1
要在参数中正确保留内部空格,应使用exec "$@"代替exec $*。从技术上讲,您需要保留的不是空格,而是IFS shell参数(默认为空格,制表符和换行符)中字符的出现。
克里斯·约翰森

点。:)
user237419

谢谢,我不知道$$参数。这可能非常有用。
rafalmag

3

在bash shell $!中,jobs -p内置的替代方法可能是。在某些情况下,shell在变量扩展之前(或代替变量扩展)解释了!in $!,从而导致意外结果。

例如,这将不起作用:

((yourcommand) & echo $! >/var/run/pidfile)

而这将:

((yourcommand) & jobs -p >/var/run/pidfile)

我认为您的意思是((yourcommand)&jobs -p> / var / run / pidfile)。
Dom

1

您可以使用类似:

$ myCommand ; pid=$!

要么

$ myCommand && pid=$!

这两个命令可以使用;或进行联合&&。在第二种情况下,仅当第一个命令成功时才设置pid。您可以从获取进程ID $pid


3
OP希望获取PID,以便稍后可以杀死它。; 和&&要求在回显$之前退出原始过程!被执行。
user9517

是的,你是对的。myCommand终止后,这将为您提供pid。
哈立德

6
在命令分隔符的左侧,引用$!之后&&;永远不会给您提供启动进程的PID。$!仅为异步启动的进程设置(例如,通常使用,&但是某些shell也具有其他方法)。
克里斯·约翰森

这很有帮助,为什么没有人投票原谅我?虽然干得好:)
寺庙

1

这有点不合逻辑,对大多数人来说不太可能。这也是一个巨大的安全风险方法,因此除非您确定自己会安全并且输入已被清理并且...好了,否则就不要这样做。

将此处的小C程序编译为一个称为start(或所需的任何)二进制文件,然后以./start your-program-here arg0 arg1 arg2 ...

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    if (argc >= 2)
    {
        printf("%lu\n", (long unsigned) getpid());
        if (execvp(argv[1], &argv[1]) < 0)
        {
            perror(NULL);
            return 127;
        }
    }
    return 0;
}

长话短说,这会将PID打印到stdout,然后将程序加载到进程中。它仍应具有相同的PID。


如何在bash变量中记录此代码返回的PID号?由于我不清楚的原因stdout,此示例中似乎未记录该消息:RESULT="$(./start_and_get_pid.out echo yo)"; echo "$RESULT"
Tfb9

@ Tfb9:老实说,我推荐其他方法之一。我在2年前写了这篇文章,到目前为止,它是目前介绍的骇客/容易出错的方法之一(或者我认为)。
tonysdg

感谢您的来信。我想我用这个解决了我的问题sleep 10 & PID_IS=$!; echo $PID_IS
Tfb9
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.