- Article pages now have a discussion option at the bottom (moderated/captcha, but no registration needed)

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.

Script execution

Your perfect Bash script executes with syntax errors

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:

Your script named "test" doesn't execute

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:

/home/user/bin/test

Globbing

Brace expansion is no globbing

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

# YOU EXPECT
# -i1.vob -i2.vob -i3.vob ....

echo -i{*.vob,}

# YOU GET
# -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:

Test-command

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

Please see:

Variables

Setting variables

The Dollar-Sign

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

# THIS IS WRONG!
$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.

# INCORRECT 1
example = Hello

# INCORRECT 2
example= Hello

# INCORRECT 3
example =Hello

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

# CORRECT 1
example=Hello

# CORRECT 2
example=" Hello"

Expanding (using) variables

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}"

# CORRECT!!!
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

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 ./script.sh):

$ cat script.sh
export hello=world

$ ./script.sh
$ 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 ./script.sh
$ echo $hello
world
$
…but then the export command is useless, at least in this case.

Please see:

Exit codes

Reacting on exit codes

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"
fi

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"
fi

Or even more:

grep ^root: /etc/passwd >/dev/null 2>&1 || 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:

Discussion

Enter your comment
 
scripting/newbie_traps.txt · Last modified: 2012/05/10 10:39 by szg
GNU Free Documentation License 1.3
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0