16

When I want to run .sh files in Terminal, I need to put sh in front of them.

Is there any way to avoid this, and thus save typing?

  • 3
    I made a folder at ~/bin to put my shell scripts into and put it on my $PATH and also attached a Folder Action to the ~/bin folder to automatically set the execute permissions because that is so easy to forget to do. – Kaydell Sep 7 '13 at 18:06
22

If you're running your script from the shell, you don't actually need the #!/bin/sh shebang as outlined in this this answer–every Unix-like system I've used, including OS X, will default to /bin/sh if no interpreter is specifically specified (though it's a good idea as non-shells won't know how to execute your script unless you give the shebang.)

You also don't need an .sh extension. You do need to set executable permissions, e.g.

$ chmod +x script.sh

(The $ is the shell prompt; I'm using it to illustrate commands you give to the interactive shell. Don't type it!)

However, I think what your confusion is is that you have created a script, e.g. script.sh, in the current directory, and are trying to execute it by simply typing script.sh. e.g.

$ cat >script.sh
echo hello, world
^D
$ chmod +x script.sh
$ script.sh
-bash: script.sh: command not found

(^D means control-D. You will find this notation in many writeups on using Unix.)

The fact that script.sh in this case is a shell script is only half the problem; your actual issue is that, by default, the shell will not search the current directory for a program. However, this works:

$ sh script.sh
hello, world

because sh is taking the script as an argument.

You can execute a script–or, again, any executable–in the current directory, if it is marked executable (i.e. chmod +x), by specifying that you want to run the one in the current directory:

$ ./script.sh
hello, world

You can also move the script to a directory on your PATH. I recommend /usr/local/bin for this if your script is intended to be used system-wide, or a bin directory in your home if the script is only for you. The latter requires you to add $HOME/bin, which expands to your new bin directory, to your PATH by adding the following lines to your .profile in your home directory:

PATH=$HOME/bin:$PATH
export PATH

Finally, you can, if you want, actually add the current directory to your PATH, which will allow you to just go to a directory containing script.sh–or any other executable–and type

$ script.sh

to execute it. However, I do not recommend this practice, as an attacker can now trick you into running an arbitrary executable by dropping an executable script (named, say, ls) into a directory you will be in. If you really want to do it, though, just add the following to your .profile:

PATH=.:$PATH
export PATH
  • Put the '.' at the end of the $PATH instead of the beginning and now you don't need to worry about a phantom 'ls'. (Although if someone can get into your system, you've got bigger issues.) – TJ Luoma Sep 10 '13 at 20:03
  • 2
    @TJ, but you do need to worry about stuff like sl or lls or l... i.e., typos. Just leave . out of $PATH. Also, you may not (or should not) necessarily trust everyone on your system. For example, suppose there's some exploit that allows an attacker to drop an arbitrary file into some directory, but they need you to execute that file in order to escalate to a better exploit (e.g., collect data and phone home). Defense in depth! – Reid Sep 10 '13 at 20:05
  • @TJLuoma What @Reid said, plus keep in mind that there are many use cases for a Unix system beyond a personal OS X desktop. Haven't tried it, but I bet even the guest account could leave one of those items in /tmp or some other world-writable directory. – zigg Sep 10 '13 at 20:10
  • If you are using a system where other people have access to directories you use, you've got larger issues. – TJ Luoma Sep 11 '13 at 20:59
  • @TJLuoma You'd best never use multiple accounts on any Unix system, then. /tmp et al. come with the territory. – zigg Sep 12 '13 at 0:44
10

You don't need to call sh if the script file is marked as executable. In that case, you can call it my name just like you would any other command from the existing shell.


To be fully proper, you'll want to do two things:

  1. Edit your script to include a shebang directive at the top of your script:

    #!/bin/sh

    ... That would tell the shell which interpreter to use to run the script; in this case, the /bin/sh executable.

  2. Mark the script as executable by you with the chmod command:

    chmod u+x scriptname.sh

Once you do both of those, you should be able to run your script by typing your script's file name on the command line. You'll need to be in the same directory as your script, unless you also take the added step of adding the containing folder to your PATH variable. If you don't care what shell runs the script, you don't need step one to specify sh but it's often better to be precise and set the "shebangsh".

  • Shebangs are not actually necessary for /bin/sh, though they don't hurt. – zigg Sep 7 '13 at 17:39
  • @zigg - You are correct. I hope Chris doesn't mind my edits… Nuke or re-edit as desired :-0 – bmike Sep 7 '13 at 18:49
  • @bmike The edits work for me. As for the shebangs, my habit was to always specify because I used to write a lot of ksh scripts back in my days on HP-UX -- but it's good to know that it isn't strictly necessary. – Chris W. Rea Sep 7 '13 at 19:16
  • 1
    I think I need to back away from my earlier statement about shebang necessity (though the rest of my answer still stands.) You can run a shebang-less script from a shell, but if you use an exec syscall, you get ENOEXEC (exec format error). Sorry about that, @bmike, @ChrisW.Rea. I'm going to go edit my answer now. – zigg Sep 7 '13 at 20:24

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .