关键词:
【中文标题】在 Linux 和 BSD 中使用和不使用 shebang 执行 Bash 脚本【英文标题】:Bash script execution with and without shebang in Linux and BSD 【发布时间】:2011-11-08 06:23:57 【问题描述】:当类似 Bash 的脚本在没有 shebang 的情况下作为二进制文件执行时,如何以及由谁来确定执行什么?
我猜想运行一个普通的脚本with shebang 是用binfmt_script Linux 模块处理的,它检查一个shebang,解析命令行并运行指定的脚本解释器。
但是当有人在没有 shebang 的情况下运行脚本时会发生什么?我测试了直接的execv
方法,发现其中没有内核魔法——即这样的文件:
$ cat target-script
echo Hello
echo "bash: $BASH_VERSION"
echo "zsh: $ZSH_VERSION"
运行只执行execv
调用的已编译 C 程序会产生:
但是,如果我从另一个 shell 脚本执行相同的操作,它会使用与原始脚本相同的 shell 解释器运行目标脚本:
$ cat test-runner.bash #!/bin/bash ./目标脚本 $ ./test-runner.bash 你好 bash:4.1.0(1)-发布 zsh:如果我对其他 shell 执行相同的技巧(例如,Debian 的默认 sh
- /bin/dash
),它也可以工作:
奇怪的是,它与 zsh 并没有像预期的那样工作,并且不遵循一般方案。看起来 zsh 毕竟在这些文件上执行了/bin/sh
:
请注意,父脚本中的 ZSH_VERSION
有效,而子脚本中的 ZSH_VERSION
无效!
shell(Bash、dash)如何确定在没有 shebang 时执行的内容?我试图在 Bash/dash 源中挖掘那个地方,但是,唉,看起来我有点迷失在那里。任何人都可以阐明确定没有 shebang 的目标文件应该作为脚本还是作为 Bash/dash 中的二进制文件执行的魔法?或者可能存在 与内核/libc 的某种交互,然后我欢迎解释它如何在 Linux 和 FreeBSD 内核/libcs 中工作?
【问题讨论】:
这是一个关于 shell 内部结构的好问题,值得找出答案。但是,我要对所有读者说:实际上,不要。使用shebang。 直接使用不同的 shell 运行target-script
可能更简单(例如 bash target-script
或 dash target-script
),而不是为每个 shell 创建一个测试运行程序。这应该给出相同的结果。
我测试了直接 execv 方法,发现其中没有内核魔法。好吧,您的 target-script
没有 shebang 行,那么您在这里期望什么内核魔法?如果你想测试内核魔法,那么你应该在你的脚本中加入 shebang 行(只是为了这个测试)。
我更喜欢这里的问题/答案:***.com/q/12296308/52074,因为链接的问题对问题/答案都有更多的赞成票,而且 IMO 的写作都更好。
【参考方案1】:
(看起来 Sorpigal 已经涵盖了它,但我已经输入了这个,它可能很有趣。)
根据Section 3.16 of the Unix FAQ,shell 首先查看幻数(文件的前两个字节)。一些数字表示二进制可执行文件; #!
表示该行的其余部分应解释为 shebang。否则,shell 会尝试将其作为 shell 脚本运行。
另外,csh
似乎在查看第一个字节,如果是#
,它会尝试将其作为csh
脚本运行。
【讨论】:
shell 首先查看幻数(文件的前两个字节) 不,它不符合您提供链接的常见问题解答条目。 Shell 首先尝试将脚本作为二进制文件执行,然后由内核来查看幻数。只有在脚本以这种方式执行失败后,shell 才会尝试将其作为脚本运行 - 如果 execl() 成功启动程序,则永远不会执行 execl() 之外的代码。跨度> @PiotrDobrogost:你说得有道理。我在那里有点粗心。但是条目的其余部分表明它比这更复杂。csh
确实查看第一个字节(不是两个字节)。另外值得注意的是,该条目与第一个 Linux 内核一样古老,因此情况可能已经发生了变化。【参考方案2】:
由于这发生在 dash 并且 dash 更简单,所以我先看那里。
好像exec.c是要看的地方,相关的函数是tryexec
,它是从shellexec
调用的,每当shell需要执行命令时就会调用它。而tryexec函数(简化版)如下:
STATIC void
tryexec(char *cmd, char **argv, char **envp)
char *const path_bshell = _PATH_BSHELL;
repeat:
execve(cmd, argv, envp);
if (cmd != path_bshell && errno == ENOEXEC)
*argv-- = cmd;
*argv = cmd = path_bshell;
goto repeat;
因此,如果出现ENOEXEC
,它总是简单地将要执行的命令替换为自身的路径(_PATH_BSHELL
默认为"/bin/sh"
)。这里真的没有魔法。
我发现 FreeBSD 在 bash
和它自己的 sh
中表现出相同的行为。
bash
处理此问题的方式类似,但要复杂得多。如果您想进一步了解它,我建议您阅读 bash 的 execute_command.c
并专门查看 execute_shell_script
和 shell_execve
。 cmets 的描述性很强。
【讨论】:
谢谢!我应该注意到execute_shell_script
中的 cmets 有点误导:没有真正的“执行模式”位检查,这一切都依赖于 execve()
返回值,更复杂的是,bash 还试图自己模拟 shebang 解析,即它不只是运行它作为脚本获得的任何东西,而是尝试查找并运行解释器,如 shebang 中所指定的那样。这个功能在 Linux 中有点用处,因为binfmt_script
已经在内核级别处理它,但它可能对其他一些操作系统有帮助?..
@GreyCat:至少我希望它在 Windows 端口中有用,所以是的。
@GreyCat 没有真正的“执行模式”位检查,这一切都依赖于execve()
返回值 根据execve(3)手册页,exec函数应如果 拒绝新进程映像文件的路径前缀中列出的目录的搜索权限,或新进程映像文件拒绝执行权限,或新进程映像文件,则失败并出现 EACCESS
错误不是常规文件,并且实现不支持执行其类型的文件。 由于 执行权限 取决于设置的执行位,人们可能会说正在检查该位,尽管不是直接检查: )在 kotlin 的 springboot 测试中使用和不使用 @Autowired Constructor 有啥区别
】在kotlin的springboot测试中使用和不使用@AutowiredConstructor有啥区别【英文标题】:Whatisdifferencebetweenusingandnotusing@AutowiredConstructorinspringboottestwithkotlin在kotlin的springboot测试中使用和不使用@AutowiredConstructor有什么区别【发布时间】:20... 查看详情
如何使用 KolodaView Swift 在卡片的左右拖动中点击喜欢和不喜欢 API
】如何使用KolodaViewSwift在卡片的左右拖动中点击喜欢和不喜欢API【英文标题】:HowtohitLikeanddislikeAPIonleftandrightdragofcardsusingKolodaViewSwift【发布时间】:2021-06-1807:44:27【问题描述】:我正在使用kolodaview进行卡片swift之类的视图。一... 查看详情
计算机基础知识
...源协议GPL许可证最严格;BSD要求最松BSD开源协议是一个给使用者很大自由的协议。基本上使用者可以”为所欲为”,可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。但当你发布使用了BSD协议... 查看详情
开源许可证gplbsdmitmozillaapache和lgpl的区别
一、如图 二、BSD开源协议BSD开源协议是一个给于使用者很大自由的协议。基本上使用者可以”为所欲为”,可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。但”为所欲为”的前提当你发... 查看详情
sed 就地标志,适用于 Mac (BSD) 和 Linux
...2011-08-0708:16:11【问题描述】:是否有在Linux和Mac上都可以使用的在没有备份的情况下调用sedtodo就地编辑?虽然OSX附带的BSDsed似乎需要sed-i\'\'…,但GNUsedLinux发行版通常将引号解释为空输入文件名(而不是备份扩展 查看详情
bsd开源协议
BSD开源协议是一个给于使用者很大自由的协议。可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。当你发布使用了BSD协议的代码,或者以BSD协议代码为基础做二次开发自己的产品时,需要满足... 查看详情
使用贪婪和不情愿的模式匹配器
】使用贪婪和不情愿的模式匹配器【英文标题】:PatternmatcherusingGreedyandReluctant【发布时间】:2014-04-1809:56:59【问题描述】:在javaregex中,我读过关于GreedyandReluctantQuantifiers的信息。他们提到了一个不情愿或“非贪婪”的量词首先... 查看详情
开源许可证gpl,bsd,mit,mozilla,apache和lgpl的区别
...的简单介绍: BSD开源协议 BSD开源协议是一个给于使用者很大自由的协议。基本上使用者可以”为所欲为”,可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。 但”为所欲为”的前... 查看详情
Remux MKV 在批处理文件中使用 mkvmerge 删除字幕和不需要的音轨
】RemuxMKV在批处理文件中使用mkvmerge删除字幕和不需要的音轨【英文标题】:RemuxMKVremovingsubtitlesandunwantedaudiotracksusingmkvmergeinbatchfile【发布时间】:2021-02-1622:55:26【问题描述】:我正在创建一个Windows批处理文件来查找文件夹中的... 查看详情
秒懂开源许可证gplbsdmitmozillaapache和lgpl的区别
...https://en.wikipedia.org/wiki/BSD_licenses)BSD开源协议是一个给于使用者很大自由的协议。基本上使用者可以”为所欲为”,可以自由的使用,修改源代码,也可以将修改后的代码作为开源或者专有软件再发布。但”为所欲为”的前提当... 查看详情
使用结果缓存和不使用结果缓存来计时我的 php 函数
】使用结果缓存和不使用结果缓存来计时我的php函数【英文标题】:TimingmyphpfunctionwithusingResultcacheandwithoutresultcache【发布时间】:2016-06-2203:31:27【问题描述】:我一直在尝试测量我的一个php函数在使用结果缓存和不使用结果缓存... 查看详情
使用多部分和不记名令牌上传图像
】使用多部分和不记名令牌上传图像【英文标题】:ImageUploadwithmultipartandbearertoken【发布时间】:2020-06-0719:09:54【问题描述】:我尝试在服务器中上传图片。它在邮递员中工作。但从我的代码来看,它的显示总是验证失败。funcupl... 查看详情
了解使用和不使用 goroutine 从通道中选择
】了解使用和不使用goroutine从通道中选择【英文标题】:Understandingselectfromchannelswithandwithoutgoroutine【发布时间】:2018-10-0907:00:09【问题描述】:你好***社区,我正在使用github.com/fsnotify/fsnotify将观察者设置为Go中的文件。我的功能... 查看详情
使用聚合和不使用聚合的选择在速度上有啥不同?
】使用聚合和不使用聚合的选择在速度上有啥不同?【英文标题】:Whatisdifferentbetweenselectwithaggregateandwithoutitinspeed?使用聚合和不使用聚合的选择在速度上有什么不同?【发布时间】:2021-11-1401:46:01【问题描述】:我有两个SQL查... 查看详情
Python 幂律符合使用 ODR 的数据中的上限和不对称错误
】Python幂律符合使用ODR的数据中的上限和不对称错误【英文标题】:Pythonpowerlawfitwithupperlimits&asymmetricerrorsindatausingODR【发布时间】:2018-03-3115:42:49【问题描述】:我正在尝试使用python将一些数据拟合到幂律中。问题是我的一... 查看详情
资料收集:学习linux/*bsd/unix的30个最佳在线文档
...术开发人员写的,更多的是为了作为参考而不是教你如何使用。手册页对于已经熟悉使用Linux、Unix和BSD操作系统的人来说是非常有用的。如果你仅仅需要知道某个命令或者某个配置文件的格式那么你可以使用手册页,但是手册页... 查看详情
java示例代码_使用Java执行系统命令(linux/bsd)
java示例代码_使用Java执行系统命令(linux/bsd) 查看详情
在帧写入循环中使用和不使用自动释放池的内存占用,为啥?
】在帧写入循环中使用和不使用自动释放池的内存占用,为啥?【英文标题】:Memoryfootprintwithandwithoutautoreleasepoolinaframewritingloop,why?在帧写入循环中使用和不使用自动释放池的内存占用,为什么?【发布时间】:2013-07-2904:26:20【... 查看详情