This is an old revision of the document!

Beginner Mistakes

Beginners tend to not think when they type, even if they know the syntax and grammar rules. Some people also mix up different programming languages (like PHP) with Bash. That's not a big deal, that's why they are "beginners".

Here are some typical traps, maybe the one you just stumbled in is in the list.

If you write Bash scripts with Bash specific syntax and features, run them with Bash, and run them with Bash in native mode.

Wrong in this case:

  • no shebang
    • depends on the OS implementation and current shell what will be used to interpret
    • of course you can always run it using the correct program: bash myscript
  • #!/bin/sh shebang
    • depends on what /bin/sh actually is, for a Bash it means a compatiblity mode, not the native mode

See also:

Give it another name. The executable test does already exist.

In Bash it's builtin, on other shell it might be ran as executable file - in any way: Bad name choice.

Workaround: You can call it using the pathname:


The following command line is not related to globbing (filename expansion):

# -i1.vob -i2.vob -i3.vob ....

echo -i{*.vob,}

# -i*.vob -i
Why? The brace expansion is simple text substitution. All possible texts formed by the prefix, the postfix and the braces themselves are generated. In the example, these are only two: -i*.vob and -i. The filename expansion happens after that, so there is a chance that -i*.vob is expanded to a filename - if you have files like -ihello.vob. But it definitely doesn't do what you thought it does.

Please see:

  • if [ $foo ] …
  • if [-d $dir] …

Please see:

The Dollar-Sign

There is no $ (dollar-sign) when you reference the name of a variable! Bash is not PHP…

$myvar="Hello world!"

A variable name preceeded with a dollar-sign always means that this variable gets expanded. In the case above, it might expand to nothing (because it wasn't set before), effectively resulting in…

="Hello world!"
…which definitely is wrong!

Everywhere you need the name of a variable, you write only the name, for example

  • (as shown above) to set variables: picture=/usr/share/images/foo.png
  • to name variables to be used by the read builtin command: read picture
  • to name variables to be unset: unset picture

Everywhere you need the content of a variable, you preceed its name with a dollar-sign, like

  • echo "The used picture is: $picture"

The Spaces

Another thing which will fail is putting any spaces around the equal-sign (=) that's used to assign a value to a variable.

example = Hello

example= Hello

example =Hello

The one and only valid form is to have no spaces between the variable name and the value to be assigned:


example=" Hello"

A typical trap for beginners is to find the correct quoting method (or to quote at all).

As noted above, when you want to expand a variable ("get the content"), the variable name needs to be preceeded by a dollar-sign. But, since Bash knows various ways to quote and does word-splitting, the result isn't always the same.

Let's define an example variable containing text with spaces:

example="Hello world"

Used formresultnumber of words
$example Hello world2
"$example" Hello world1
\$example $example1
'$example' $example1

But, if you use several forms of the parameter expansion, you must use the name (PATH) of the referenced variables/parameters. You must not try to use their expansion ($PATH):

# WRONG!!!
echo "The first character of PATH is ${$PATH:0:1}"

echo "The first character of PATH is ${PATH:0:1}"

Note that if you are using variables in arithmetic expressions, then the bare name is allowed:

((a=$a+7))         # Add 7 to a
((a = a + 7))      # Add 7 to a.  Identical to the previous command.
((a += 7))         # Add 7 to a.  Identical to the previous command.

a=$((a+7))         # POSIX-compatible version of previous code.

Please see:

Exporting a variable means to give newly created (child-)processes a copy of that variable. It's not meant to "copy back" a variable created in a child process to the parent. The following example does not work, since the variable hello is set in a child process (the process you execute to start that script ./

$ cat
export hello=world

$ ./
$ echo $hello

Exporting is a one-way. The direction is parent process ⇒ child process, and not vice versa. The above example will work, when you don't execute the script, but include ("source") it:

$ source ./
$ echo $hello
…but then the export command is useless, at least in this case.

Please see:

If you just want to react on an exit code, disregarding the specific value of the exit code, you don't need to bother with $? in a test command like this:

grep ^root: /etc/passwd >/dev/null 2>&1
if [ $? -neq 0 ]; then
  echo "root was not found - check the pub at the corner"

This code can be simplified:

if ! grep ^root: /etc/passwd >/dev/null 2>&1; then
  echo "root was not found - check the pub at the corner"

If you need the specific value of $?, there's no other chance, yes. But if you're just interested in conditional code based on "true/false" exit code meaning, there's no need to bother with $? directly.

See also:

This website uses cookies for visitor traffic analysis. By using the website, you agree with storing the cookies on your computer.More information
U.Lickert, 2015/09/24 19:37

Reacting to exit codes

You may use the specific value, and do much more by enclosing a group of commands in { }. (and still a 1-liner not pushing the rest of code out of sight)

Note the ';' after the last command, it is necessary

grep ^root: /etc/passwd >/dev/null 2>&1 || { rc=$?; echo "That search returned a '$rc' to me. Maybe time for the pub."; return $rc; }
You could leave a comment if you were logged in.
  • scripting/newbie_traps.1321096593.txt
  • Last modified: 2011/11/12 11:16
  • by thebonsai