outer.sh
:
ls -l /proc/$$/exe
coproc cat
./inner.sh
kill $!
inner.sh
:
ls -l /proc/$$/exe
set | grep COPROC || echo No match found
coproc cat
kill $!
当我运行时./outer.sh
,会打印以下内容:
lrwxrwxrwx 1 joe joe 0 Jun 16 22:47 /proc/147876/exe -> /bin/bash
lrwxrwxrwx 1 joe joe 0 Jun 16 22:47 /proc/147879/exe -> /bin/bash
No match found
./inner.sh: line 3: warning: execute_coproc: coproc [147878:COPROC] still exists
既然COPROC
和COPROC_PID
没有在孩子中设置,那么它如何知道来自父母的那个能够给我这个警告呢?
另外,我发现如果我添加#!/bin/bash
到 的顶部inner.sh
,或者如果我调用bash ./inner.sh
而不只是./inner.sh
from outer.sh
,那么警告就会消失。既然它是通过 bash 子进程运行的,为什么这会改变任何东西呢?
答案1
没有 shebang 的脚本意味着由符合 POSIX 的sh
解释器进行解释。这实际上是编写 POSIX 脚本的 POSIX 方式,POSIX 没有指定 shebangs,尽管在实践中使用 shebangs 更便携/可靠,这里有一个很好的例子。
bash shell 就是这样一个 POSIX sh 解释器。 bash(某些版本、某些自定义构建和某些环境)实际上是我所知道的唯一的 FLOSS shell认证的运行时为合规sh
(而不是运行为时bash
)。
当执行无 shebang 的脚本时,bash 在execve()
返回 ENOEXEC 并检查它看起来不像二进制文件后,在其子文件中解释它,通过尝试将其状态重置为默认值来模拟执行。
然而,这意味着该脚本在运行时bash
被解释为 bash 脚本而不是 POSIX sh 脚本,除非bash
本身以 POSIX 模式运行(例如作为sh
其本身调用时)。
$ cat a
alias uname='echo hi'
uname
$ zsh -c ./a
hi
$ sh ./a
hi
$ bash -c ./a
Linux
$ (exec -a sh bash -c ./a)
hi
了解当 调用时如何a
被解释为语言(忽略别名)而不是 sh 语言。bash
bash
~$ strace -qqfe execve bash -c ./a
execve("/usr/bin/bash", ["bash", "-c", "./a"], 0x7fff0081a820 /* 66 vars */) = 0
execve("./a", ["./a"], 0x55b18b3a4660 /* 66 vars */) = -1 ENOEXEC (Exec format error)
[pid 123559] execve("/usr/bin/uname", ["uname"], 0x55b18b3a4660 /* 66 vars */) = 0
Linux
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=123559, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
查看如何bash
不执行 sh
解释脚本。
您得到的事实warning: execute_coproc: coproc [147878:COPROC] still exists
是一个错误,bash
无法正确重置其状态。
无论如何,coproc
都不是sh
关键字,因此在无 shebang 的脚本中没有其位置。coproc
来自zsh
(而协进程来自 ksh),尽管bash
的实现完全不同,所以你应该#! /bin/bash -
在这里有一个 shebang。
使用bash ./inner.sh
或使用 shebang,可以正确执行新的解释器实例,并execve()
完全正确地擦除进程内存。