我想来源(即不称呼)来自任何 shell 的脚本(bash/csh 是主要目标,但 Fish、zsh、ksh 和 rc 也很有趣)。
我希望脚本是否可以是单个文件 - 即不是每个 shell 方言的文件。
我可以这样做吗?
我正在考虑类似的事情:
if shell is bash then
# bash code here
else
if shell is csh then
# csh code here
else
if shell is xxxsh then
# xxxsh code here
fi
endif
fi
所以我可以这样做:
csh% source my_script
bash$ . my_script
问题是 - 当然,if
每种方言都不相同,所以我需要使用对每个 shell 都有效的语法。
编辑
检测外壳是第一步在运行时确定脚本中的 shell做得很好。
同样重要的一步是如何引用 shell 不同部分的代码,以免混淆其他 shell。想一想:如何在 bash 部分中包含<<here_document
在 bash 中合法但在任何其他 shell 中非法的所有组合中的所有字符,而不会使其他 shell 感到困惑。任何答案/链接答案均未涵盖这一点。
答案1
您不应该为每个 shell 编写代码片段,而应该只编写可被大多数 shell 解释的可移植代码。你应该看看POSIX Shell 命令语言。这是 shell(遵循 POSIX)应如何解释代码的标准。
许多 shell(例如bash
)可以配置为像 POSIX shell 一样运行。每个 shell 都有其独特的功能和特定的符号。在可移植脚本中完全避免使用它们。
答案2
对于类似 csh 和类似 Bourne 的情况,你可以这样做:
start=:#||:<<"goto end="
echo "(t)csh code here"
if { bindkey >& /dev/null } then
echo tcsh
endif
goto end=
echo Bourne-like code here
if [ -n "$BASH_VERSION" ]; then
echo bash
fi
end=:
解释:
start=:
将被视为 中的标签声明csh
和 中的变量赋值sh
,因此是两个无害的操作。- 在 中
start=:#
,它#
被视为评论领导者csh
,但在 中则不然,sh
因为它不是一个单独的标记。所以后面的内容被注释掉了,csh
但没有被注释掉sh
。 - 变量赋值
sh
具有成功退出状态(只要赋值不涉及命令替换),因此||
不会运行操作员的命令权限。 - 该
:<<"goto end="...goto end
命令不会运行,但会被解析和忽略("goto end"
引用的事实阻止了此处文档内的各种扩展)。但请注意:在 Bourne shell 中(并且仅在 Bourne shell 中),仍然会创建一个临时文件。 - 因此整个部分直到
goto end=
被类似 Bourne 的 shell 忽略并由csh
.goto end=
导致csh
忽略 和 之间的部分end=:
(类似 Bourne 的 shell 中无害的变量赋值)。
添加的 shell 支持越多,它就越棘手。fish
特别是,它非常棘手,因为它会检查整个脚本的语法,即使它不运行的部分也是如此。
也可以看看:
- 在运行时确定脚本中的 shell多语言代码的示例。
- 如何在不知道远程用户的登录 shell 的情况下通过 ssh 执行任意简单命令?用于 sh/csh/rc/fish 系列的代码(当我们讨论
parallel
调用 shell 时我写了那一篇)
答案3
我认为您想要这样做的原因是为了可移植性。上面的回答给你提供了线索。如果您假设 Bourne shell (sh) 是最小公分母,并且“随处可用”(Linux、Solaris、Unix、AIX 等),那么您可以#!/bin/sh
在脚本开头添加 ,并编写所有脚本仅使用 sh 的功能。 (我认为在 Linux 中,sh 只是 bash 的别名,因为 bash 是 Bourne shell 的超集,但不管怎样,它应该仍然可以工作。)
稍微改进一下,假设它sh
始终存在,但您可能需要一些更高级 shell 的额外功能和便利性。因此,您可以为您支持的每个 shell 编写一个版本的脚本,例如 script.bash、script.zsh、script.ksh、script.csh 等。每个脚本都以自己的 shebang 行开头,然后在脚本中。 sh 文件你会说这样的话:
#!/bin/sh
if [[ -e /bin/ksh ]] ; then
./script.ksh
exit
elif [[ -e /bin/csh ]] ; then
./script.csh
exit
fi
ETC。
进一步的建议是研究auto-tools工具链的源代码。当你执行 ./configure 时运行的那些东西;制作 ;进行安装。 GNU 人员做了很多聪明的事情来确保他们的脚本可以在所有环境中运行。