为什么 $0 和所有参数从运行 bash 脚本时消失(如 ps 所示)?

为什么 $0 和所有参数从运行 bash 脚本时消失(如 ps 所示)?

我有一个 bash 脚本(第一行#! /bin/bashdo-doruns,我从 bash 命令行调用它./do-doruns arg1 arg2 ...

该脚本依次调用 script ./dorun a1 a2 a3 ...,并且还有更多级别的 script ,然后调用编译后的程序。

当使用 和 进行查看时psdorun在其下调用的所有正在运行的事物都会显示ps其名称和命令行参数,例如,

/bin/bash /home/moss/path-to-dorun/dorun a1 a2 a3

等等。但没有do-doruns显示任何线路。它肯定正在运行(等待子进程完成)。我能理解为什么它论点可能会消失(尽管单独的测试表明,shift从 来看,使用 bash 不会导致参数消失ps),但它的$0(命令名称)不会消失。

虽然这不是我工作中的障碍,但这是我想了解的一个奇怪的谜团。事物的版本如下:

  • uname -a显示:Linux moss-Ubuntu-SMCSim 5.4.0-81-generic #91-Ubuntu SMP Thu Jul 15 19:09:17 UTC 2021 x86_64 x86_64 x86_64 GNU/Linux

  • bash --version显示:GNU bash, version 5.0.17(1)-release (x86_64-pc-linux-gnu)

  • ps --version显示(也许很奇怪):ps from procps-ng UNKNOWN

    这种行为不仅限于ps-top也显示了它,所以它看起来更基本。

  • 最后,如果重要的话,这一切都在 VirtualBox 虚拟机中运行。

    vboxmanage --version显示:7.0.4r154605

答案1

它可能取决于脚本的第一行的样子,因为这会更改选项使用的命令名称ps -C,以及可能如何将其与 grep 匹配(取决于您使用的标志)。

使用类似的东西

#!/bin/bash

会将命令名称更改为脚本的名称,因此这将显示为 do-dorun。

使用

#!/bin/env bash

意味着命令名称将保留为 bash。

两者的命令行看起来都是一样的,例如/bin/bash myscriptname.sh arg1 arg2 arg3

答案2

进一步的研究,即将我的脚本减少到最小的测试用例,表明这种行为是由于使用 bash 的eval内置函数造成的。最小的测试用例是:

#! /bin/bash
eval foo xyz

哪里foo有其他要运行的脚本。 (这也可能发生在程序中,尽管我没有专门测试它。)

似乎发生的情况是eval导致 shellbash使用-c命令行选项 fork-exec a 来运行命令行。看来这样的 bash 调用会导致命令行显示为空的作业,我想是因为命令行已被读取,然后 bash 只执行所需的程序。

因此,如果我不想要这种行为,似乎我应该避免使用eval.

谢谢你帮我想清楚!

答案3

有一种简单的方法可以更改 shell 的命令行,exec因为后面的命令exec会替换调用 shell。考虑到关于美化/proc/pid/cmdline这里的打印输出的评论是版本2。但我不明白,使用 eval 执行在变量中准备的命令如何会导致像exec.

示例 v2:

#!/bin/bash

identifyMyself() {
echo "My cmdline: $0 My arg1: $1 My PID: $BASHPID"
echo "Kernel says:"
#cat -A /proc/$BASHPID/cmdline
#echo
readarray -d '' -t cmdline < /proc/$$/cmdline
#declare -p cmdline
printf '%s ' "${cmdline[@]}"
echo
}

identifyMyself "$1"

if [ -z "$1" ]
then
echo "calling myself with long parameters"
"$0" arg1 arg2 arg3 arg4
fi

if [ -z "$1" ]
then
dorun="$0 arg1 arg2 arg3 arg4"
echo "calling myself with long parameters by evaling a variable"
eval "${dorun}"
fi

if [ -z "$1" ]
then
echo "I am still here"
identifyMyself "$1"
fi

if [ -z "$1" ]
then
echo "execing myself with long parameters"
exec "$0" arg1 arg2 arg3 arg4
fi

if [ -z "$1" ]
then
echo "now I am gone"
identifyMyself "$1"
fi

输出:

$ ./bashtestv2_pid.sh
My cmdline: ./bashtestv2_pid.sh My arg1:  My PID: 1444428
Kernel says:
/bin/bash ./bashtestv2_pid.sh 
calling myself with long parameters
My cmdline: ./bashtestv2_pid.sh My arg1: arg1 My PID: 1444429
Kernel says:
/bin/bash ./bashtestv2_pid.sh arg1 arg2 arg3 arg4 
calling myself with long parameters by evaling a variable
My cmdline: ./bashtestv2_pid.sh My arg1: arg1 My PID: 1444430
Kernel says:
/bin/bash ./bashtestv2_pid.sh arg1 arg2 arg3 arg4 
I am still here
My cmdline: ./bashtestv2_pid.sh My arg1:  My PID: 1444428
Kernel says:
/bin/bash ./bashtestv2_pid.sh 
execing myself with long parameters
My cmdline: /home/user/bin/bashtestv2_pid.sh My arg1: arg1 My PID: 1444428
Kernel says:
/bin/bash /home/user/bin/bashtestv2_pid.sh arg1 arg2 arg3 arg4 

请注意,当命令行发生变化时,执行后 PID 如何保持不变。其eval行为与正常调用类似。这就是你在评论中所说的。但在你自己的回答中,你的说法恰恰相反。请给出eval导致隐式 的语句的示例exec

相关内容