编写可在任何shell上运行的shell脚本(使用多个shebang行?)


19

我刚刚开始更深入地了解Shell脚本,而且我一直只是将我的脚本扔到一个文件中,对其进行标记chmod +x,然后完成/path/to/script.sh,然后让任何默认的解释器使用它,我认为这是zsh,因为那是我用我的壳。显然/bin/sh,即使我从zsh提示符执行脚本,这似乎也是默认设置,因为我开始在脚本中放入特定于zsh的内容,除非我运行,否则它会失败zsh /path/to/script.sh

明确地说,这是我的问题:

  1. #!/path/to/shell开始没有shebang行()时,哪个shell执行脚本?我假设/bin/sh但无法确认。
  2. 在编写可在任何平台上运行的shell脚本方面,什么被称为“最佳实践”?(好的,这是开放式的)
  3. 如果zsh不可用,是否有可能编写尝试使用zsh并退回到bash的脚本?我已经尝试过放置两条shebang行,如下所示,但是bad interpreter: /bin/zsh: no such file or directory如果我在没有zsh的计算机上尝试,那只会出错。

    #!/bin/zsh

    #!/bin/bash

Answers:


26

当开始没有shebang行(#!/ path / to / shell)时,哪个shell执行脚本?我假设/ bin / sh但我无法确认。

内核拒绝执行此类脚本并返回ENOEXEC,因此确切的行为取决于您从中运行此类脚本的程序。

  • bash 4.2.39 –自用
  • busybox-ash 1.20.2 –自用
  • 破折号0.5.7 –运行/ bin / sh
  • 鱼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。(这在exec(3p)中有说明。)

在编写可在任何平台上运行的shell脚本方面,什么被称为“最佳实践”?(好的,这是开放式的)

要么坚持sh,只有POSIX定义的功能,或者只是去完全的bash(这是广泛使用的),并提到它在你的要求,如果发送邮件。

(现在我想到了,Perl或Python)将更加可移植,更不用说具有更好的语法了。)

始终添加shebang行。如果使用bash或zsh,请使用#!/usr/bin/env bash而不是对外壳程序的路径进行硬编码。(但是,保证POSIX shell位于at /bin/sh,因此请跳过env这种情况。)

(不幸的是,甚至/bin/sh并不总是相同。GNUautoconf程序必须处理许多不同的问题。)

如果zsh不可用,是否有可能编写尝试使用zsh并退回到bash的脚本?我已经尝试过放置两个shebang行,如下所示,但是由于解释器错误而导致错误:/ bin / zsh:如果在没有zsh的计算机上尝试,则没有这样的文件或目录

射帮线只能有一条。换行符之后的所有内容甚至都不会被内核读取,并被shell视为注释。

可以编写一个脚本,运行的#!/bin/sh,检查其外壳是可用的,并且运行exec zsh "$0" "$@"exec bash "$0" "$@"取决于结果。但是,bash和zsh所使用的语法在许多地方都非常不同,因此我不建议您出于自己的理智而这样做


1
您如何跟踪每个外壳收到ENOEXEC时实际调用的内容?
swrobel 2012年

2
@SWrobel:使用strace -f -e fork,clone,execve/bin/sh失败后,一些shell继续执行。其他人自己解释脚本。
grawity 2012年

1
@SWrobel:另一种方法是运行包含的脚本readlink /proc/$$/exe
grawity 2012年


1
对于cshtcsh,其行为取决于脚本是否以#(该情况下,他们调用自己而不是sh来解释脚本)开头。那可以追溯到csh支持注释而不是Bourne shell的时候,因此#暗示它是csh脚本。
sch

2

1)您正在其中运行的当前shell(可能是任何shell)

2)坚持使用相同的外壳类型(bash / dash / ash / csh /任何类型),并确保“支持的平台”默认安装要使用的外壳。另外,尝试在系统上使用常用命令。避免使用机器特定的选项。

3)的确没有“ if-then-else”逻辑interpreter directive。您应该指定一个您希望支持的所有系统上都应存在的外壳……即,#!/bin/bash或者指定一个通用的外壳,#!/bin/sh只要您的脚本在所有外壳上都相当通用即可。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.