在这种情况下,bash 如何知道其父进程的协进程,以及为什么 shebang 行会更改它?

在这种情况下,bash 如何知道其父进程的协进程,以及为什么 shebang 行会更改它?

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

既然COPROCCOPROC_PID没有在孩子中设置,那么它如何知道来自父母的那个能够给我这个警告呢?

另外,我发现如果我添加#!/bin/bash到 的顶部inner.sh,或者如果我调用bash ./inner.sh而不只是./inner.shfrom 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 语言。bashbash

~$ 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()完全正确地擦除进程内存。

相关内容