0

我发现 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

我想知道为什么。

4

1 回答 1

2

让我们从你的第二个案例开始,即你有的地方#!/bin/bash,因为它实际上是第一个更容易处理的案例。

随着#!/bin/bash

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

没有#!/bin/bash

现在转向另一个#!/bin/bash丢失的。这个案子比较微妙。

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

>>> 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 分叉一个子shell,

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

  • 但是,执行不成功,这意味着子shell仍在运行

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

整体效果与案例非常相似#!/bin/bash,但由于子shell 刚刚通过fork 原始bash 进程启动,因此它具有相同的命令行,即 just bash,没有任何命令行参数。如果您在ps uxf(进程的树状视图)的输出中查找此子shell,您将看到它就像

bash
 \_ bash

而在#!/bin/bash你得到的情况下:

bash
 \_ /bin/bash ./sample.sh
于 2020-06-05T19:35:35.703 回答