当我运行这个脚本时,打算运行直到被杀死......
# foo.sh
while true; do sleep 1; done
...我无法使用以下方法找到它ps ax
:
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21110 pts/3 S+ 0:00 grep --color=auto foo.sh
...但是如果我只是将常见的“ #!
”标题添加到脚本中...
#! /usr/bin/bash
# foo.sh
while true; do sleep 1; done
...然后脚本变得可以通过相同的ps
命令找到...
>./foo.sh
// In a separate shell:
>ps ax | grep foo.sh
21319 pts/43 S+ 0:00 /usr/bin/bash ./foo.sh
21324 pts/3 S+ 0:00 grep --color=auto foo.sh
为什么会这样?
这可能是一个相关的问题:我认为“ #
”只是一个评论前缀,如果是这样,“ #! /usr/bin/bash
”本身只不过是一个评论。但是“ #!
”是否具有比仅仅作为评论更大的意义?
当当前的交互式 shell 是
bash
,并且您运行没有#!
-line 的脚本时,bash
将运行该脚本。该过程将在ps ax
输出中显示为 justbash
。在另一个终端:
有关的:
相关部分构成
bash
手册:这意味着
./foo.sh
在命令行上运行,当foo.sh
没有#!
-line 时,与在子 shell 中运行文件中的命令相同,即用正确的
#!
线指向 eg/bin/bash
,它就像在做当 shell 脚本以 开头时
#!
,就 shell 而言,第一行是注释。然而,前两个字符对系统的另一部分有意义:内核。这两个字符#!
被称为shebang。要了解 shebang 的作用,您需要了解程序是如何执行的。从文件执行程序需要内核的操作。这是作为
execve
系统调用的一部分完成的。内核需要验证文件权限,释放与当前在调用进程中运行的可执行文件相关的资源(内存等),为新的可执行文件分配资源,并将控制权转移给新程序(以及更多我就不提了)。系统调用替换当前运行进程的execve
代码;有一个单独的系统调用fork
来创建一个新进程。为了做到这一点,内核必须支持可执行文件的格式。该文件必须包含机器代码,以内核理解的方式组织。shell 脚本不包含机器代码,因此不能以这种方式执行。
shebang 机制允许内核将解释代码的任务推迟到另一个程序。当内核看到可执行文件以 开头时
#!
,它会读取接下来的几个字符并将文件的第一行(减去前导#!
和可选空格)解释为另一个文件的路径(加上参数,我不会在这里讨论) )。当内核被告知要执行该文件/my/script
,并且它看到该文件以该行开头时#!/some/interpreter
,内核/some/interpreter
将使用参数执行/my/script
。然后由它/some/interpreter
来决定/my/script
它应该执行的脚本文件。如果一个文件既不包含内核理解格式的本机代码,也不以 shebang 开头怎么办?好吧,那么该文件是不可执行的,并且
execve
系统调用失败并显示错误代码ENOEXEC
(可执行格式错误)。这可能是故事的结尾,但大多数 shell 都实现了回退功能。如果内核返回
ENOEXEC
,shell 会查看文件的内容并检查它是否看起来像一个 shell 脚本。如果 shell 认为文件看起来像一个 shell 脚本,它会自己执行它。它如何做到这一点的细节取决于外壳。ps $$
您可以通过添加脚本来查看正在发生的一些事情,还可以通过查看进程,strace -p1234 -f -eprocess
其中 1234 是 shell 的 PID。在 bash 中,这种回退机制是通过调用
fork
而不是execve
. 子 bash 进程自行清除其内部状态并打开新的脚本文件以运行它。因此,运行脚本的进程仍然使用原始 bash 代码映像和最初调用 bash 时传递的原始命令行参数。ATT ksh 的行为方式相同。相比之下,Dash
ENOEXEC
通过调用/bin/sh
作为参数传递的脚本的路径来做出反应。换句话说,当您从 dash 执行 shebangless 脚本时,它的行为就好像该脚本有一个带有#!/bin/sh
. Mksh 和 zsh 的行为方式相同。在第一种情况下,脚本由当前 shell 的一个分叉子运行。
您应该首先运行
echo $$
,然后查看以您的 shell 的进程 id 作为父进程 id 的 shell。