82

I have written a simple script. When I runsh <myscriptname.sh>, i got the correct output, but when I run ./<myscriptname.sh>, I got an error.

What is difference between when I do sh and ./?

6 Answers6

71

When you run any script by passing the filename to the script interpreter program, you are running the interpreter program with the script as an argument passed into it. For example this would look like the process 'sh' with the argument 'filename.sh'. The sh interpreter is opening the file.

On the other hand if you run the script itself, the system calls out to the interpreter program specified and feeds in the scripts contents. In this case the process looks like 'filename.sh' with no arguments.

You should make sure you have a bang line:

#!/bin/bash
# bash script here

A bang line is the very first line in the script and starts with the same two characters #!, these are what the system reads when it tries to execute the script and then the system passes the the script to the program immediately after. Note that this line isn't anything to do with bash and works just as well for python and perl, even though they're very different languages. You would use #!/usr/bin/python for example and then follow it with python code.

Once you have your script, make sure you have set execute permissions:

chmod a+x filename.sh

Then you can run the script as its own process:

./filename.sh

Or put the file into a known location with a nice program name, like /usr/sbin and run from anywhere:

sudo cp filename.sh /usr/sbin/program-name
program-name

And this is really the practical benefit of using the bang line with the right permissions - it's all about deployment. It's very hard to get users to run a script if they have to remember what program to run the script with. Remember to give a full path to the script every time they want to run it. Where as putting it in /usr/local/bin for example, and making it executable, can save an awful lot of grief for people trying to use your script. These programs then become available to all users on the your computer.

It's also good for identification. If you go into the top program, a script run without the bang line will just have the name of the interpreter i.e. bash, perl or python. But if a script is run with the right permissions, then the name of the script shows.

Note: If you want to distribute a script that's accessible to everyone, then please create a man page and a deb package to install it. We need to reduce the number of random scripts online and increase the number of debs which can be uninstalled.

  • 1
    You can also use a personal bin folder: create a bin folder to your home folder log out and log in back and you should then be able run any scripts in that folder without sh or ./ . – papukaija Jan 23 '11 at 16:46
  • Of course, but adding your home folder to your PATH can be seen to be a bit of a headache. Better get things installed. Although there was some talk of adding ~/.local/bin to the path by default to allow local user installs of packages. – Martin Owens -doctormo- Jan 23 '11 at 17:28
  • Note that the default interpreter is bash, not sh. – Nathan Osman Jan 24 '11 at 02:13
  • 2
    Don't put extensions on scripts, especially not when you put it in PATH. – geirha Jan 24 '11 at 07:46
  • For a noob like me... could you explain the PRACTICAL differences between both situations? In other words.... what can I do with one method that I can't with the other, and vice versa? – luri Jan 24 '11 at 09:03
  • @luri - I've expanded that for you. – Martin Owens -doctormo- Jan 24 '11 at 14:23
  • ty... to much noobism inside me in scripting issues :)... I had put the bang line within my 2 or 3 scripts without knowing why. – luri Jan 24 '11 at 14:26
  • You can give an example with a different bang line, like '#!/usr/bin/python'. I believe that it will make the answer easier to understand as python syntax is wildly different from bash. – Javier Rivera Jan 25 '11 at 08:04
  • @Javier - It's exactly the same as you show in your comment. The python syntax has no effect on the bang line at all. – Martin Owens -doctormo- Jan 25 '11 at 14:57
  • @Martin - Yes I know. I was trying to explain is that a python script is very different from a bash script (while bash, csh or even fish are somewhat similar). So a python example could illustrate better the work of the bang line (IMHO). – Javier Rivera Jan 25 '11 at 15:09
  • @Javier - I disagree, there isn't any bash scripting in the example already. One would have to make an almighty leap to assume python was like bash because a function of execution in the system was the same. – Martin Owens -doctormo- Jan 25 '11 at 16:52
  • @Martin: Nice :). It's your answer ;). I still feel that it will be clearer, but... – Javier Rivera Jan 26 '11 at 08:14
  • 1
    /usr/local/bin is probably better then /usr/sbin -- it indicates that the program is local to this machine rather than being part of the distribution. – glenn jackman Apr 07 '11 at 20:02
45

The short version:

  • sh is the command-line interpreter (dash).
    Running sh my_script makes dash interpret the script.

  • ./ tries to find out which interpreter to use, by looking at the first line. E.g. #!/bin/bash, or even #!/bin/ruby (as opposed to running ruby my_script).

Melebius
  • 11,431
  • 9
  • 52
  • 78
8

The difference you do is,

  • with sh, you're running a program that will interpret the lines in your script just as it you would have typed them on the interactive prompt of the terminal,

  • with ./ you're making a shortcut assuming that the script is just right here in the current directory you're sitting in AND it will be executable (because for instance you issued chmod +x myscript.sh), saving you invaluable time for future times :-)

meduz
  • 188
3

There are three main reasons you might be getting an error:

  • the file is not executable
    run chmod +x <myscriptname.sh> to fix that
  • the partition does not allow running scripts (is mounted "noexec")
    copy the script to /usr/local/bin
  • the #! line has an error
    make sure the first line is #!/bin/sh or #!/bin/bash

If your first line looks right, but still isn't working, make sure the file doesn't have DOS line endings.

The error would look something like this:

$ ./myscript.sh
bash: ./myscript.sh: /bin/bash^M: bad interpreter: No such file or directory

You can fix it by running dos2unix <myscriptname.sh>, or if you don't have that,
perl -p -i -e 's/\r\n$/\n/' <myscriptname.sh>.

Mikel
  • 6,558
0

And the answer is that sh is name for very popular shell. But outdated and replaced by others. Nowadays sh is linked to others shells installed on machine. e.g. I have bash putted there. Running any shell from sh usually trigger some 'compatibility' mode with original 'shell' behavior.

So solution is quite simple. Look what's behind sh command (ls -al /bin/sh), and put #!/bin/whatever_you_find_there as first line (or if there is something like that in your script edit it).

And alternatively there may be some bug in script itself. Like dependence that is met by sh, but not interpreter that is actually used.

przemo_li
  • 143
0
mkdir ~/bin ; cp myscript.sh ~/bin/

echo "export PATH="$PATH:/home/$USER/bin" >> ~/.profile ; source ~/.profile ; 

Not /usr/sbin, that's for non-essential administrative tools, /usr/local/bin is a better choice if you don't want to have a ~/bin/, but avoiding sudo as much as possible is advisable.

  • On Ubuntu, the default ~/.profile already has code for adding ~/bin, if it exists, to PATH. On another note, don't put extensions on scripts. – geirha Jan 24 '11 at 07:44
  • 1
    I was referencing the <myscriptname.sh>, but what is the rationale for not putting extensions on scripts? I usually do because having the plain text files visible like that keeps me from trying to edit binaries that I also keep in ~/bin/. (ls ~/bin/|wc -l = 428) I put a lot of stuff in there ;) – Joey1978 Jan 24 '11 at 08:01
  • Imagine you write a script to achieve a specific task. Then you realize writing this particular script in python will make it much more efficient, so you rewrite it in python. Now you have two choices. 1) Leave the now very misleading .sh-extension, or 2) rename the script and search and replace all uses of the script to use the new name. If you look at the scripts in /bin and /usr/bin you'll see they don't use extensions. – geirha Feb 02 '11 at 08:49