我有一个bash
名为 的脚本test.sh
,以startproc
.如果我#!/usr/bin/env bash
在 中使用 shebang test.sh
,该/proc/<PID>/cmdline
文件如下所示:
SERVER:~ # cat /proc/29481/cmdline
bash/root/user/test.shSERVER:~ #
SERVER:~ #
现在,当我将 shebang 行更改为 时#!/bin/bash
,/proc/<PID>/cmdline
文件如下:
SERVER:~ # cat /proc/29729/cmdline
/bin/bash/root/user/test.shSERVER:~ #
SERVER:~ #
是什么导致了这种行为?文件的内容/proc/<PID>/cmdline
取决于脚本shebang吗?问题是,如果使用前一个选项checkproc
,killproc
或者startproc
无法检测到该test.sh
服务。我使用 openSUSE 11.4 和sysvinit-tools-2.88-37.47.1.x86_64
.
答案1
是的,cmdline
具体取决于exec*
进行的系统调用,并且完全限定的路径可能env(1)
与其路径搜索中出现的路径不同。
bash-4.1$ cat aaa
#!/bin/bash
xxd /proc/$$/cmdline
bash-4.1$ cat bbb
#!/usr/bin/env bash
xxd /proc/$$/cmdline
bash-4.1$ ./aaa
0000000: 2f62 696e 2f62 6173 6800 2e2f 6161 6100 /bin/bash../aaa.
bash-4.1$ ./bbb
0000000: 6261 7368 002e 2f62 6262 00 bash../bbb.
bash-4.1$
strace
显示细节:
bash-4.1$ strace ./aaa 2>&1 | grep exec
execve("./aaa", ["./aaa"], [/* 57 vars */]) = 0
bash-4.1$ strace ./bbb 2>&1 | grep exec
execve("./bbb", ["./bbb"], [/* 57 vars */]) = 0
execve("/sbin/bash", ["bash", "./bbb"], [/* 57 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/sbin/bash", ["bash", "./bbb"], [/* 57 vars */]) = -1 ENOENT (No such file or directory)
execve("/usr/local/sbin/bash", ["bash", "./bbb"], [/* 57 vars */]) = -1 ENOENT (No such file or directory)
execve("/bin/bash", ["bash", "./bbb"], [/* 57 vars */]) = 0
bash-4.1$
如果这是一个仅限 Linux 的脚本,那么 的位置bash
可能不会改变(例外:在其他地方有一个包含bash
, 的某种软件库)。因此,使用完全限定路径可能是最有意义的,因为这可以避免env(1)
在路径搜索中出现混乱,并获得与 init 工具的兼容性。
答案2
进程的命令行由argv
参数的元素组成execve
系统调用。此参数是一个从 0 开始编号的数组,其中编号 1 到 n 的元素是调用命令时传递的参数,元素 0 由发出调用的 shell 或其他程序选择execve
。按照惯例,元素 0 是用于指定命令的字符串。
Shebang 行由内核处理。内核插入一个参数 0,它是#!
魔术前缀后指定的路径。因此,如果您使用中的/root/user/test.sh
一个参数运行,则使用两个参数 0= 、 1=进行调用。当以 开头时,内核会看到 shebang并将参数列表重写为 0= 、 1= 、 2= 。foo
startproc
startproc
execve
/root/user/test.sh
foo
/root/user/test.sh
/bin/bash
/root/user/test.sh
/bin/bash
/root/user/test.sh
foo
如果 shebang 行是#!/bin/env bash
,则内核会在参数列表中插入两项:程序和参数。 (Linux 此处仅限于单个参数。)因此,在本例中,调用将转换为 0= /bin/env
、 1= bash
、 2= /root/user/test.sh
、 3= foo
。该env
程序完成其工作并发出一个新的execve
系统调用 0= bash
(遵守参数 0 是用于指定命令的路径的约定)、 1= /root/user/test.sh
、 2= foo
。
在所有情况下,/root/user/test.sh
都将是该过程的第一个参数。