我刚刚开始深入研究 shell 脚本,我总是将脚本放入一个文件中,标记它chmod +x
,然后完成/path/to/script.sh
,让默认解释器执行它,我假设它是 zsh,因为我的 shell 就是用的这个。显然,/bin/sh
即使我从 zsh 提示符执行脚本,它似乎也是默认的,因为我开始在脚本中加入 zsh 特定的内容,除非我运行,否则它会失败zsh /path/to/script.sh
。
为了切中要点,以下是我的问题:
#!/path/to/shell
当开头没有 shebang 行 ( ) 时,哪个 shell 会执行脚本?我假设是/bin/sh
这样,但无法确认。- 就编写可在任何平台上运行的 shell 脚本而言,什么被视为“最佳实践”?(好吧,这有点开放式)
是否可以编写一个脚本,尝试使用 zsh,如果 zsh 不可用则返回到 bash?我尝试过放置两个 shebang 行,如下所示,但
bad interpreter: /bin/zsh: no such file or directory
如果我在没有 zsh 的机器上尝试,它只会出错。#!/bin/zsh
#!/bin/bash
答案1
当开头没有 shebang 行 (#!/path/to/shell) 时,哪个 shell 会执行脚本?我假设是 /bin/sh,但我无法确认。
内核拒绝执行此类脚本并返回 ENOEXEC,因此具体行为取决于运行此类脚本的程序从。
- bash 4.2.39 – 使用自身
- busybox-ash 1.20.2 – 使用自身
- dash 0.5.7 – 运行 /bin/sh
- fish 1.23.1 – 抱怨 ENOEXEC,然后指责错误的文件
- AT&T ksh 93u+2012.08.01 – 使用自身
- mksh R40f – 运行 /bin/sh
- pdksh 5.2.14 – 运行 /bin/sh
- sh-heirloom 050706 – 自身使用
- tcsh 6.18.01 – 运行 /bin/sh
- zsh 5.0.0 – 运行 /bin/sh
- cmd.exe 5.1.2600 – 看起来很有趣。
在glibc、函数execv()
或execve()
仅返回 ENOEXEC。但execvp()
会隐藏此错误代码并自动调用 /bin/sh。(这在执行(3p)。
就编写可在任何平台上运行的 shell 脚本而言,什么被视为“最佳实践”?(好吧,这有点开放式)
要么坚持sh
使用 POSIX 定义的特性,要么就完全狂欢(广泛可用)并在分发时在您的要求中提及它。
(现在我想起来,Perl——或者可能是 Python——的可移植性会更强,更不用说具有更好的语法了。)
总是添加 shebang 行。如果使用 bash 或 zsh,请使用#!/usr/bin/env bash
而不是硬编码 shell 的路径。(但是,POSIX shell 保证位于/bin/sh
,因此在这种情况下跳过env
。)
(不幸的是,即使/bin/sh
并不总是相同的。GNU自动配置程序必须处理许多不同的怪癖。
是否可以编写一个脚本,尝试使用 zsh,如果 zsh 不可用,则返回到 bash?我尝试过输入两行 shebang,如下所示,但它只是出错了错误的解释器:/bin/zsh:没有此文件或目录如果我在没有 zsh 的机器上尝试它。
只能有一个 shebang 行;换行符后的所有内容都不会被内核读取,并且会被 shell 视为注释。
它是可能的编写一个脚本,以 运行#!/bin/sh
,检查哪个 shell 可用,并根据结果运行exec zsh "$0" "$@"
或。但是,bash 和 zsh 使用的语法在各个地方有很大不同,因此我会exec bash "$0" "$@"
不建议这样做为了您自己的理智。
答案2
1) 你当前正在运行的 shell。(无论是什么 shell)
2) 坚持使用相同的 shell 类型(bash/dash/ash/csh/无论你喜欢哪种类型),并确保你的“支持平台”默认安装你希望使用的 shell。此外,尽量使用系统上常用的命令。避免使用特定于机器的选项。
3) 实际上没有“if-then-else”逻辑interpreter directive
。您应该指定一个存在于您希望支持的所有系统上的 shell... 即,只要您的脚本在所有 shell 中都相当通用,#!/bin/bash
就可以指定一个通用的 shell 。#!/bin/sh