【问题标题】:ps/pgrep cannot find running script missing #!/bin/bashps/pgrep 找不到运行脚本缺少#!/bin/bash
【发布时间】:2020-06-06 03:35:35
【问题描述】:

我发现 ps 或 pgrep 找不到没有“#!/bin/bash”的运行脚本

这是一个sample.sh:

while true
do
    echo $(date)                                                                                                                                                         
done

启动脚本(ubuntu 18.04,Linux 版本 4.15.0-101-generic):

$echo $BASH
/bin/bash
./sample.sh

打开另一个终端,ps只找到命令grep

$ps -aux |grep sample.sh
16887  0.0  0.0  16184  1008 pts/4    S+   07:12   0:00 grep --color=auto sample

pgrep 什么也没找到

$pgrep sample
$

但如果我在 sample.sh 中添加“#!/bin/bash”,现在一切正常:

#!/bin/bash      <-----add this line                                                                                                                                                        
while true
do
    echo $(date)
done

我想知道为什么。

【问题讨论】:

  • 我想这取决于您拥有的 bash 版本,因为无论使用 bash 5.0.11,我都无法获得 sample.sh 的 PID。但是如果我把printf 'boogaloo' &gt; /proc/self/comm放在sample.sh的第一行,我可以通过pgrep boogaloo得到它的PID。
  • @camino :一个区别当然是没有#!行,脚本将由 sh 运行,但这仍然不能解释我的区别。如果您通过sh sample.sh 显式启动脚本,与bash sample.sh 相比,会发生什么情况?
  • @user1934428 sh sample.sh,ps aux|grep sample.sh 有效。

标签: linux shell


【解决方案1】:

让我们从你的第二种情况开始,即你确实#!/bin/bash,因为它实际上更容易处理第一个。

#!/bin/bash

当您执行以#!/path/to/interpreter 开头的脚本时,Linux 内核将理解此语法并为您调用指定的解释器,就像您在命令行开头显式添加/path/to/interpreter 一样.因此,如果您的脚本以#!/bin/bash 开头,如果您使用ps ux,您将看到命令行/bin/bash ./sample.sh

没有#!/bin/bash

现在转到缺少#!/bin/bash 的另一个。这种情况比较微妙。

既不是已编译的可执行文件也不是以#! 行开头的文件Linux 内核根本无法执行。下面是一个尝试在没有 #!/bin/bash 行的情况下从 python 脚本运行 sample.sh 的示例:

>>> import subprocess
>>> p  = subprocess.Popen("./sample.sh")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/subprocess.py", line 394, in __init__
    errread, errwrite)
  File "/usr/lib/python2.7/subprocess.py", line 1047, in _execute_child
    raise child_exception
OSError: [Errno 8] Exec format error

为了表明这不仅仅是一个 python 问题,这里有一个完全相同的演示,但是来自一个 C 程序。这是C代码:

#include <stdio.h>
#include <unistd.h>

int main() {

  execl("./sample.sh", "sample.sh", NULL);

  /* exec failed if reached */
  perror("exec failed");
  return 1;  
}

这是输出:

exec failed: Exec format error

因此,当您运行脚本时,这里发生的情况是 因为您是从 bash shell 调用它,所以 bash 通过在尝试“执行”后直接运行命令来提供一些容错" 脚本失败。

更详细的情况是:

  • bash forks 一个子shell,

  • 在子 shell 中,它会立即调用 Linux 内核来“执行”您的可执行文件,如果成功,这将结束该(子 shell)进程并将其替换为运行可执行文件的进程

  • 但是,exec 成功,这意味着 subshel​​l 仍在运行

  • 此时子shell 只是读取脚本中的命令并开始直接执行它们。

整体效果与#!/bin/bash 的情况非常相似,但由于子shell 刚刚通过fork 原始bash 进程启动,它具有相同的命令行,即只是bash,没有任何命令行参数。如果你在ps uxf 的输出中寻找这个子shell(你的进程的树状视图),你会看到它就像

bash
 \_ bash

而在#!/bin/bash 的情况下,您会得到:

bash
 \_ /bin/bash ./sample.sh

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-27
    • 2013-04-06
    • 1970-01-01
    • 1970-01-01
    • 2021-11-08
    • 1970-01-01
    相关资源
    最近更新 更多