138

Why do all script files start with

#!/bin/sh

or with

#!/bin/csh

Is that required? What's the purpose of this? And what's the difference between the two?

miller
  • 1,636
  • 3
  • 26
  • 55
One Two Three
  • 22,327
  • 24
  • 73
  • 114
  • 1
    For a csh script, you should use `#!/bin/csh -f`; the `-f` tells the shell not to source the user's `.login` and `.cshrc`, which makes the script run faster and avoids dependencies on the user's setup. (Or better yet, don't write csh scripts.) Don't use `-f` for sh or bash scripts; it doesn't have the same meaning. – Keith Thompson May 30 '13 at 15:07
  • it's a fair question and a classic but "is it required" can also be answered empirically. Mostly... no, not at all. – Dan Rosenstark Apr 13 '23 at 21:16

3 Answers3

133

This is known as a Shebang:

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

#!interpreter [optional-arg]

A shebang is only relevant when a script has the execute permission (e.g. chmod u+x script.sh).

When a shell executes the script it will use the specified interpreter.

Example:

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

$ chmod u+x foo.sh
$ ./foo.sh
  1
kwarrick
  • 5,930
  • 2
  • 26
  • 22
  • @Kolob Canyon you do not need to, but it can help some editors with syntax highlighting (although there are usually other ways to achieve the same thing): https://unix.stackexchange.com/a/88730/193985 – Braham Snyder Oct 01 '17 at 22:33
59

The #! line tells the kernel (specifically, the implementation of the execve system call) that this program is written in an interpreted language; the absolute pathname that follows identifies the interpreter. Programs compiled to machine code begin with a different byte sequence -- on most modern Unixes, 7f 45 4c 46 (^?ELF) that identifies them as such.

You can put an absolute path to any program you want after the #!, as long as that program is not itself a #! script. The kernel rewrites an invocation of

./script arg1 arg2 arg3 ...

where ./script starts with, say, #! /usr/bin/perl, as if the command line had actually been

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

Or, as you have seen, you can use #! /bin/sh to write a script intended to be interpreted by sh.

The #! line is only processed if you directly invoke the script (./script on the command line); the file must also be executable (chmod +x script). If you do sh ./script the #! line is not necessary (and will be ignored if present), and the file does not have to be executable. The point of the feature is to allow you to directly invoke interpreted-language programs without having to know what language they are written in. (Do grep '^#!' /usr/bin/* -- you will discover that a great many stock programs are in fact using this feature.)

Here are some rules for using this feature:

  • The #! must be the very first two bytes in the file. In particular, the file must be in an ASCII-compatible encoding (e.g. UTF-8 will work, but UTF-16 won't) and must not start with a "byte order mark", or the kernel will not recognize it as a #! script.
  • The path after #! must be an absolute path (starts with /). It cannot contain space, tab, or newline characters.
  • It is good style, but not required, to put a space between the #! and the /. Do not put more than one space there.
  • You cannot put shell variables on the #! line, they will not be expanded.
  • You can put one command-line argument after the absolute path, separated from it by a single space. Like the absolute path, this argument cannot contain space, tab, or newline characters. Sometimes this is necessary to get things to work (#! /usr/bin/awk -f), sometimes it's just useful (#! /usr/bin/perl -Tw). Unfortunately, you cannot put two or more arguments after the absolute path.
  • Some people will tell you to use #! /usr/bin/env interpreter instead of #! /absolute/path/to/interpreter. This is almost always a mistake. It makes your program's behavior depend on the $PATH variable of the user who invokes the script. And not all systems have env in the first place.
  • Programs that need setuid or setgid privileges can't use #!; they have to be compiled to machine code. (If you don't know what setuid is, don't worry about this.)

Regarding csh, it relates to sh roughly as Nutrimat Advanced Tea Substitute does to tea. It has (or rather had; modern implementations of sh have caught up) a number of advantages over sh for interactive usage, but using it (or its descendant tcsh) for scripting is almost always a mistake. If you're new to shell scripting in general, I strongly recommend you ignore it and focus on sh. If you are using a csh relative as your login shell, switch to bash or zsh, so that the interactive command language will be the same as the scripting language you're learning.

zwol
  • 135,547
  • 38
  • 252
  • 361
  • Recent versions of Linux do allow the specified interpreter to be a script. It's common practice to omit the space after the `#!`; no comment on whether that's good style. See [this question](http://unix.stackexchange.com/q/29608/10454) and [my answer](http://unix.stackexchange.com/a/29620/10454) for a discussion of the pros and cons of the `#!/usr/bin/env` hack. – Keith Thompson May 30 '13 at 15:00
  • @KeithThompson I'm under the impression Linux is the *only* common Unix variant that allows the interpreter to be a script, so it's still not something to rely on. Since I wrote this I've myself encountered a situation where `#!/usr/bin/env` was the Right Thing, but it remains my opinion that it is almost always a bad idea. – zwol May 30 '13 at 15:04
7

This defines what shell (command interpreter) you are using for interpreting/running your script. Each shell is slightly different in the way it interacts with the user and executes scripts (programs).

When you type in a command at the Unix prompt, you are interacting with the shell.

E.g., #!/bin/csh refers to the C-shell, /bin/tcsh the t-shell, /bin/bash the bash shell, etc.

You can tell which interactive shell you are using the

 echo $SHELL

command, or alternatively

 env | grep -i shell

You can change your command shell with the chsh command.

Each has a slightly different command set and way of assigning variables and its own set of programming constructs. For instance the if-else statement with bash looks different that the one in the C-shell.

This page might be of interest as it "translates" between bash and tcsh commands/syntax.

Using the directive in the shell script allows you to run programs using a different shell. For instance I use the tcsh shell interactively, but often run bash scripts using /bin/bash in the script file.

Aside:

This concept extends to other scripts too. For instance if you program in Python you'd put

 #!/usr/bin/python

at the top of your Python program

Levon
  • 138,105
  • 33
  • 200
  • 191
  • So is it required? How do I know which shell I'm really using? – One Two Three May 14 '12 at 21:14
  • So if I'm writing the scripts for someone to use on their machine, and I don't know what shell they are using. (This person, unfortunately, is clueless about this stuff, thus all he can do is to run the script without changing a thing). Can I do something like `#! $SHELL` ? Would this put the correct shell in the Shebang? – One Two Three May 14 '12 at 21:25
  • 1
    @OneTwoThree Most systems have the standard shells, if you write a bash or csh script you'll be ok. What shell they are using interactively **doesn't** matter, that is the beauty of the e.g., `!#/bin/bash`directive. It tells the system what shell to use to execute your shell script. – Levon May 14 '12 at 21:27
  • The value of `$SHELL` doesn't necessarily tell you which shell you're running at the moment; it normally tells you your *default* shell. tcsh sets `$version` and `$tcsh`; bash sets `$BASH_VERSION`. Not all shells necessarily have similar mechanisms. – Keith Thompson May 30 '13 at 15:05
  • 1
    @OneTwoThree: The `#!` line has to match the syntax of the script, *not* the interactive shell used by whoever is running the script. – Keith Thompson May 30 '13 at 15:06