Shell脚本标头(#!/ bin / sh与#!/ bin / csh)


91

为什么所有脚本文件都以

#!/bin/sh

或搭配

#!/bin/csh

这是必需的吗?目的是什么?两者之间有什么区别?


1
对于csh脚本,应使用#!/bin/csh -f;。在-f告诉shell不要源的用户的.login.cshrc,这使得脚本运行速度更快,避免了用户的设置的依赖关系。(或者更好的是,不要编写csh脚本。)不要-f用于sh或bash脚本;它没有相同的含义。
基思·汤普森

Answers:


98

这称为Shebang

http://en.wikipedia.org/wiki/Shebang_(Unix)

#!解释器[optional-arg]

仅当脚本具有执行权限时(例如chmod u + x script.sh),shebang才有意义。

当shell执行脚本时,它将使用指定的解释器。

例:

#!/bin/bash
# file: foo.sh
echo 1

$ chmod u+x foo.sh
$ ./foo.sh
  1

@Kolob Canyon并不是必需的,但是它可以帮助某些编辑器突出显示语法(尽管通常还有其他方法可以实现相同的功能):unix.stackexchange.com/a/88730/193985
Braham Snyder

41

#!行告诉内核(特别是execve系统调用的实现)该程序是用解释性语言编写的。后面的绝对路径名标识解释器。编译成机器代码的程序开始用不同的字节序列-在大多数现代Unix系统,7f 45 4c 46^?ELF),其识别它们的方式。

你可以把一个绝对路径,以任何你想要的程序#!,只要该程序本身不是一个#!脚本。内核重写了

./script arg1 arg2 arg3 ...

./script以开头的地方#! /usr/bin/perl,就像命令行实际上是

/usr/bin/perl ./script arg1 arg2 arg3

或者,如您所见,您可以#! /bin/sh用来编写旨在由解释的脚本sh

#!,如果你线只处理直接调用(该脚本./script的命令行); 该文件还必须是可执行文件(chmod +x script)。如果这样做,则sh ./script#!行不是必需的(如果存在,将被忽略),并且该文件不必是可执行的。该的功能是直接让你调用解释语言程序,而不必知道的都写在什么语言。(做grep '^#!' /usr/bin/*-你会发现非常多的股票程序中使用此功能是事实。)

以下是使用此功能的一些规则:

  • #!必须是非常前两个字节的文件中。特别是,该文件必须采用与ASCII兼容的编码(例如UTF-8可以使用,但UTF-16不能使用),并且不能以“字节顺序标记”开头,否则内核不会将其识别为#!脚本。
  • 之后的路径#!必须是绝对路径(以开头/)。它不能包含空格,制表符或换行符。
  • #!和之间放置一个空格是一种很好的样式,但不是必需的/。请勿在该处放置多个空间。
  • 您不能将shell变量放在#!行上,它们不会被扩展。
  • 您可以在绝对路径后放置一个命令行参数,并用一个空格隔开。与绝对路径一样,此参数不能包含空格,制表符或换行符。有时这是使事情正常进行所必需的(#! /usr/bin/awk -f),有时只是有用的(#! /usr/bin/perl -Tw)。不幸的是,您不能在绝对路径后放置两个或多个参数。
  • 有人会告诉您使用#! /usr/bin/env interpreter而不是#! /absolute/path/to/interpreter这几乎总是一个错误。它使程序的行为取决于$PATH调用脚本的用户的变量。并不是所有的系统都env首先存在。
  • 需要setuidsetgid特权的程序不能使用#!; 它们必须编译为机器代码。(如果您不知道这setuid是什么,请不必为此担心。)

关于csh,它sh大致与Nutrimat Advanced Tea Substitution替代品与茶有关。与交互式使用相比,它具有(或更确切地说已经具有;现代的实现sh已获得)许多优势sh,但是使用它(或其后代tcsh)进行脚本编写几乎总是一个错误。如果您一般不熟悉Shell脚本,强烈建议您忽略它,并专注于sh。如果您使用csh亲戚作为登录外壳,请切换到bashzsh,以便交互式命令语言将与您正在学习的脚本语言相同。


Linux的最新版本确实允许将指定的解释器作为脚本。通常的做法是在#!;之后省略空格。没有评论这是否是好的风格。请参阅此问题我的答案,以讨论#!/usr/bin/env黑客的利弊。
基思·汤普森

@KeithThompson我给人的印象是Linux是唯一允许解释器成为脚本的常见Unix变体,因此它仍然不值得依赖。自从我写这篇文章以来,我自己遇到的情况#!/usr/bin/env是对的,但是我仍然认为这几乎总是一个坏主意。
zwol

4

这定义了您用于解释/运行脚本的shell(命令解释器)。每个外壳程序与用户交互并执行脚本(程序)的方式略有不同。

在Unix提示符下键入命令时,您正在与外壳进行交互。

例如,#!/bin/csh是指C壳,/bin/tcsht壳,/bin/bashbash壳等。

您可以判断出您正在使用哪个交互式外壳

 echo $SHELL

命令,或者

 env | grep -i shell

您可以使用chsh命令更改命令外壳。

每个都有一个稍微不同的命令集和分配变量的方式以及它自己的一组编程结构。例如,带有bash的if-else语句看起来与C-shell中的语句不同。

该页面可能会引起关注,因为它在bash和tcsh命令/语法之间“翻译”。

在shell脚本中使用指令可以使您使用其他shell运行程序。例如,我以tcsh交互方式使用外壳程序,但是经常使用脚本文件中的/ bin / bash运行bash脚本。

在旁边:

这个概念也扩展到其他脚本。例如,如果您使用Python编程,

 #!/usr/bin/python

在Python程序的顶部


那是必需的吗?我怎么知道我真正在使用哪个外壳?
一二三2012年

因此,如果我正在编写脚本供某人在其计算机上使用,而我不知道他们使用的是什么shell。(不幸的是,这个人对此东西一无所知,因此他所能做的就是在不做任何更改的情况下运行脚本)。我可以做类似的事情#! $SHELL吗?这会把正确的炮弹放到射帮里吗?
一二三2012年

1
@OneTwoThree大多数系统都有标准的外壳程序,如果您编写bash或csh脚本,就可以了。什么shell他们使用交互式没有的事,那就是如的美丽!#/bin/bash指令。它告诉系统使用什么Shell来执行Shell脚本。
莱文,2012年

的值$SHELL不一定告诉您当前正在运行哪个shell;它通常会告诉您您的默认外壳。tcsh集$version$tcsh; 打击套$BASH_VERSION。并非所有的外壳都必须具有类似的机制。
基思·汤普森

1
@OneTwoThree:该#!行必须匹配脚本的语法,而不是运行脚本的人使用的交互式外壳。
基思·汤普森
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.