Answers:
有什么比这更简单的echo $!
呢?作为一行:
myCommand & echo $!
myprogram
这样的东西真是太棒了
&
作为上一行开始命令,否则echo基本上返回空白。
command & echo $!
冻结该步骤的执行:(
sh -c
和exec
获取命令的PID 。要启动myCommand
,以便在其开始运行之前先打印其PID,可以使用:
sh -c 'echo $$; exec myCommand'
这将启动一个新的外壳程序,打印该外壳程序的PID,然后使用exec
内置命令用命令替换该外壳程序,以确保其具有相同的PID。当您的shell使用exec
内置命令运行命令时,您的shell 实际上正在成为该命令,而不是派生自己的新副本的更为常见的行为,该副本具有自己独立的PID,然后成为命令。
我发现,这比涉及异步执行(带有&
),作业控制或使用搜索的替代方案要简单得多ps
。这些方法很好,但是除非您有特定的理由使用它们(例如,也许该命令已经在运行,在这种情况下搜索其PID或使用作业控制才有意义),我建议您首先考虑采用这种方法。(而且我当然不会考虑编写复杂的脚本或其他程序来实现此目的)。
此答案包括此技术的示例。
即使您正在使用的Shell是Bourne样式,因此支持exec
具有这些语义的内建函数,您通常也不应为此避免使用sh -c
(或等效方法)创建一个新的单独的 Shell进程,因为:
myCommand
,就没有shell等待运行后续命令了。用替换自身后将sh -c 'echo $$; exec myCommand; foo
无法尝试运行。除非您编写的脚本是最后一个运行该脚本的脚本,否则不能只在运行其他命令的shell中使用它。foo
myCommand
echo $$; 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
命令的环境吗?
exec
命令。但是,这种方法在myCommand
启动其他流程(您需要使用这些流程)时不起作用。当我发出以这种方式获得的kill -INT <pid>
位置pid
时,信号不会到达由开始的子进程myCommand
,而如果我myCommand
在当前会话中运行并按Ctrl + C,则信号会正确传播。
sh -c 'echo $$; exec /usr/local/bin/mbdyn -f "input.file" -o "/path/to/outputdir" > "command_output.txt" 2>&1 &'
将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
exec "$@"
代替exec $*
。从技术上讲,您需要保留的不是空格,而是IFS shell参数(默认为空格,制表符和换行符)中字符的出现。
您可以使用类似:
$ myCommand ; pid=$!
要么
$ myCommand && pid=$!
这两个命令可以使用;
或进行联合&&
。在第二种情况下,仅当第一个命令成功时才设置pid。您可以从获取进程ID $pid
。
$!
之后&&
或;
永远不会给您提供启动进程的PID。$!
仅为异步启动的进程设置(例如,通常使用,&
但是某些shell也具有其他方法)。
这有点不合逻辑,对大多数人来说不太可能。这也是一个巨大的安全风险方法,因此除非您确定自己会安全并且输入已被清理并且...好了,否则就不要这样做。
将此处的小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。
stdout
,此示例中似乎未记录该消息:RESULT="$(./start_and_get_pid.out echo yo)"; echo "$RESULT"
sleep 10 & PID_IS=$!; echo $PID_IS