分发Shell脚本的正确方法


8

如果shell的行为可以被修改set并因此无法预测,那么分发shell脚本的最合适方法是什么?

例如,rm *.txtset -f已运行的环境中不会按预期执行。rm *.txt在任何环境下,如何确保删除当前目录中的所有文本文件?

在总体分发外壳脚本之前,我应如何确保它们能够按预期运行?


shell_state=$(set +o) ... 脚本 ...eval "$shell_state"
mikeserv

您可以在脚本本身的顶部运行这些命令,或修改#!来指定所需的shell选项。
HalosGhost

Answers:


6

Shell脚本通常被视为与其他任何类型的可执行文件相同,例如二进制文件,Python脚本,Perl脚本或任何其他类型的脚本。它们在顶部有一个Shebang,它指示内核通过Shell执行它们。期望它们以与其他任何命令相同的方式被调用。

这样,每次调用脚本时都会启动一个新的外壳程序,并且诸如set -f调用外壳程序或系统中任何其他外壳程序实例中存在的任何设置都是不相关的。

当然,用户可以源代码而不是运行脚本,例如:

. /path/to/your/script

或在具有非默认设置的shell中执行以下命令:

sh -f /path/to/your/script

但是这些都不被认为是正常的方式或调用您的脚本的方式,这样做的用户应该期望得到的结果是什么。

请注意,有一些脚本旨在提供源代码,而不是执行脚本,因为它们的目的是针对诸如更改cwd或设置环境变量之类的事情,这些脚本必须反映在采购外壳程序的环境中,但是这些脚本只占少数。并且通常是作为协议的一部分完成的。可以将这些文件视为它们希望由其来源的任何系统的“插件”,而不是独立脚本。例如,/etc/rc*.d名称以结尾结尾的文件.sh是由启动脚本子系统提供的,而不是执行的,并且有记录表明,如果将这样一个名称的文件放入其中,将会发生这种情况。/etc/rc*.d当它完成时,它是故意完成的。旨在源文件而不是以这种方式执行的命名文件的约定在其他地方也遵循,但不是通用的。

的确,打算以这种方式获取的文件必须照顾好调用者环境中可能会影响Shell行为的设置,但是理想情况下,协议必须保证可预测的执行环境。


1
zsh具有一个功能,可以将选项还原为本地上下文(emulate -L zsh)中的已知集,该功能在zsh随附的所有扩展中都广泛使用并以zsh代码编写。如果设置一些选项,请查看bash完成系统如何惨遭失败failglob
斯特凡Chazelas

即使调用了Shell以解释脚本,某些外壳也会读取您的自定义文件。这就是为什么您通常使用#! /bin/csh -f而不是#! /bin/csh例如。
斯特凡Chazelas

@StéphaneChazelas哈哈,是的。当然,这就是为什么使用csh是一个坏主意的一部分,对吧?:-)但是我想zsh也有,形式为zshenvzshenv出于这个原因,请小心输入!
Celada 2014年

顺便说一句,@StéphaneChazelas,这不是您第一次emulate -L通过评论我的一个答案教给我一些我不知道的东西(在这种情况下),我对此表示赞赏。
Celada 2014年

好吧,~/.zshenv(您要为以您的名字调用的所有zsh(无论是否交互)强制设置(通常为env vars,通常不用于解决其他一些非zsh问题)通常与~/.zshrc(交互式自定义外壳程序),因此它是一项功能,而不是错误。(您可以使用zsh -f)一个错误是某些非交互式bash外壳读取您的外壳~/.bashrc(例如通过ssh,您必须添加代码以防止这种情况),或者某些交互式bash(登录的外壳)不读取它(同样,您必须在其中添加代码~/.bash_profile来解决)。
斯特凡Chazelas
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.