如何在Unix控制台或Mac终端上运行一个shell脚本?

605
我知道它,忘记它,然后重新学习。现在是把它写下来的时候了。
7个回答

1110

使用以下命令来运行一个非可执行的sh脚本:

sh myscript

要运行一个非可执行的bash脚本,请使用:

bash myscript

要启动一个可执行文件(任何拥有可执行权限的文件);您只需指定其路径:

/foo/bar
/bin/bar
./bar

要使脚本可执行,需要赋予它必要的权限:

chmod +x bar
./bar

当一个文件可执行时,内核负责决定如何执行它。对于非二进制文件,这是通过查看文件的第一行完成的。第一行应该包含一个hashbang:

#! /usr/bin/env bash

哈希注释告诉内核运行哪个程序(在这种情况下,命令/usr/bin/env将以参数bash的形式运行)。然后,脚本会作为第二个参数传递给程序,并随后将您提供给脚本的所有参数作为子参数。

这意味着每个可执行的脚本都应该有一个哈希注释。如果没有,您没有告诉内核它是什么,因此内核不知道要使用哪个程序来解释它。可能是bashperlpythonsh或其他程序。(实际上,内核通常会使用用户的默认shell来解释文件,这非常危险,因为可能根本不是正确的解释器,或者它可能能够解析其中的一些内容,但具有细微的行为差异,例如shbash之间的情况)。

关于/usr/bin/env的说明

最常见的情况是,你会看到以下这样的哈希注释:

#!/bin/bash
结果是内核将运行程序/bin/bash来解释脚本。不幸的是,默认情况下并不总是安装bash,而且也不一定在/bin中可用。虽然在Linux机器上通常都是这样的,但在其他一些POSIX机器上,bash会以各种位置出现,例如/usr/xpg/bin/bash/usr/local/bin/bash
为了编写可移植的bash脚本,我们因此不能依赖硬编码bash程序的位置。 POSIX已经有一个处理这个问题的机制:PATH。其思想是将程序安装在PATH中的一个目录中,系统应该能够按名称找到你要运行的程序。
可悲的是,你不能只是这样做:
#!bash
内核不会(也许有些会)为你执行PATH搜索。但是,有一个程序可以为你执行PATH搜索,它叫做env。幸运的是,几乎所有系统都在/usr/bin中安装了env程序。因此,我们使用硬编码路径启动env,然后它会执行PATH搜索以查找bash并运行它,以便解释你的脚本。
#!/usr/bin/env bash

这个方法有一个缺点:根据POSIX规范,hashbang只能有一个参数。在这种情况下,我们将bash作为env程序的参数使用。这意味着我们没有空间来传递参数到bash。所以无法将类似#!/bin/bash -exu的内容转换成这种方式。你需要将set -exu放在hashbang之后。

这个方法还有另一个优点:某些系统可能会预装/bin/bash,但用户可能不喜欢它,可能觉得它有漏洞或已过时,并且可能已经在其他地方安装了自己的bash。这在OS X(Macs)上经常发生,因为Apple预装了过时的/bin/bash,而用户使用Homebrew之类的工具安装了最新的/usr/local/bin/bash。当您使用env方法进行PATH搜索时,您考虑到用户的偏好并使用他选择的bash而不是系统提供的bash。


77
感谢您抽出时间给一个简单问题写下了一份很好的答案。我将为您进行翻译,让内容更加通俗易懂,但不会改变原意。 - P-A
6
如果我使用 zsh 作为我的 shell,我应该使用 hashbang #! /usr/bin/env zsh 吗? - stefmikhail
6
无论使用哪种Shell解释器来调用脚本都没有关系,如果脚本内的代码应该由Z shell执行,则应该使用 #! /usr/bin/env zsh - johnsyweb
1
我倾向于忘记,但知道命令的含义会帮助我回忆起来。+1 说明。 - Angelin Nadar
2
@Carpetsmoker 这是正确的,而且不仅限于hashbang。Bash脚本应该始终使用UNIX行尾,否则每个命令的最后一个参数都会附加\r,就像hashbang命令名一样。 - lhunath
显示剩余5条评论

104

启动shell脚本 'file.sh':

sh file.sh

bash file.sh

另一种选择是使用chmod命令设置可执行权限:

chmod +x file.sh

现在,请按照以下方式运行.sh文件:

./file.sh

21
对于Bourne Shell来说:
sh myscript.sh

对于bash:

bash myscript.sh

谢谢您回答这个非常明显的问题。对于像我这样的Mac用户来说,在使用过程中很容易忘记旧的Unix命令。 - P-A

10

如果你希望脚本在当前shell中运行(例如,你希望它能够影响你的目录或环境),那么应该这样说:

. /path/to/script.sh
或者
source /path/to/script.sh

请注意/path/to/script.sh可以是相对路径,例如. bin/script.sh将在当前目录下的bin目录中运行script.sh


7
在引用或使用相对路径时,一定要非常小心。你应该始终以"./"开头。如果不这样做,而且相对路径中不包含任何斜杠,则会在当前目录之前从PATH中引用某些内容!这非常容易被滥用,因此需要特别注意。 - lhunath

5

首先,给予权限以执行:


chmod +x script_name
  1. 如果脚本没有可执行权限:
    要运行 sh 脚本文件:
    sh script_name
    要运行 bash 脚本文件:
    bash script_name
  2. 如果脚本有可执行权限:
    ./script_name

注意:您可以使用“ls -a”命令检查文件是否可执行。


1
文件扩展名.command被分配给Terminal.app。双击任何.command文件将执行它。

1

稍作补充,如果要在同一文件夹中运行解释器,仍需在脚本中使用#!hashbang

例如,将从/usr/bin复制的php7.2可执行文件和一个名为hello的脚本放在同一文件夹中。

#!./php7.2
<?php

echo "Hello!"; 

运行它:

./hello

这两个行为完全相等:

./php7.2 hello

适当的解决方案和良好的文档可以是工具linuxdeploy和/或appimage,这是使用此方法的底层实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接