Parameters

Parameters should be seen as a sort of named space in memory where you can store your data. Generally speaking, they will store string data, but can also be used to store integers or arrays.



Special Parameters and Variables

Let's get our vocabulary straight before we get into the real deal. There are Parameters and Variables. Variables are actually just a kind of parameters: parameters that are denoted by a name. Parameters that aren't variables are called Special Parameters. I'm sure you'll understand things better with a few examples:

    $ # Some parameters that aren't variables:
    $ echo My shell is $0, and was started with these options: $-
    My shell is -bash, and was started with these options: himB
    $ # Some parameters that ARE variables:
    $ echo I am $LOGNAME, and I live at $HOME.
    I am lhunath, and I live at /home/lhunath.

Please note: Unlike PHP/Perl/... parameters do NOT start with a $-sign. The $-sign you see in the examples merely causes the parameter that follows it to be expanded. Expansion basically means that the shell replaces it by its content. As such, LOGNAME is the parameter (variable), that contains your username. $LOGNAME will be replaced with its content; which in my case, is lhunath.

I think you've got the drift now. Here's a summary of most Special Parameters:

And here are some examples of Variables that the shell initializes for you:

Of course, you aren't restricted to only these variables. Feel free to define your own:

    $ country=Canada
    $ echo "I am $LOGNAME and I currently live in $country."
    I am lhunath and I currently live in Canada.

Notice what we did to assign the value Canada to the variable country. Remember that you are NOT allowed to have any spaces before or after that equals sign!

    $ language = PHP
    -bash: language: command not found
    $ language=PHP
    $ echo "I'm far too used to $language."
    I'm far too used to PHP.

Remember that BASH is not Perl or PHP. You need to be very well aware of how expansion works to avoid big trouble. If you don't, you'll end up creating very dangerous situations in your scripts, especially when making this mistake with rm:

    $ ls
    no secret  secret
    $ file='no secret'
    $ rm $file
    rm: cannot remove `no': No such file or directory

Imagine we have two files, no secret and secret. The first contains nothing useful, but the second contains the secret that will save the world from impending doom. Unthoughtful as you are, you forgot to quote your parameter expansion of file. BASH expands the parameter and the result is rm no secret. BASH splits the arguments up by their whitespace as it normally does, and rm is passed two arguments; 'no' and 'secret'. As a result, it fails to find the file no and it deletes the file secret. The secret is lost!






Variable Types

Although BASH is not a typed language, it does have a few different types of variables. These types define the kind of content they have. They are stored in the variable's attributes.

Attributes are settings for a variable. They define the way the variable will behave. Here are the attributes you can assign to a variable:

Arrays are basically lists of strings. They are very convenient for their ability to store different elements without relying on a delimiter. That way, you don't need to worry about the fact that this delimiter could possibly end up being part of an element's content and thus split that element up:

    $ files='one:two:three:four'

Here we try to use a string to contain a list of files. To do that, we need to rely on a delimiter to keep the files apart. We choose ':'. As a result, we cannot add any files to the list that have a ':' in their filename. That's why arrays are so convenient:

    $ files=( 'one' 'two' 'three' 'four' '5: five' )

As shown above, you can assign arrays using (...). In this case, elements are separated by whitespace; but you can protect an element's whitespace with quotes. If you want to use some form of expansion to assign values to an array, rather than literal, be aware that BASH will obviously need to perform some form of word splitting to figure out which parts of your expansion should be put in which elements of the array:

    $ files='one:two:three:four'
    $ IFS=:
    $ files=( $files )

For this word splitting, BASH looks at the first character in IFS again. There, it finds the delimiter to use for splitting the result of the expansion up in elements.

Defining variables as integers has the advantage that you can leave out some syntax when trying to assign or modify them:

    $ a=5; a+=2; echo $a; unset a
    52
    $ a=5; let a+=2; echo $a; unset a
    7
    $ declare -i a=5; a+=2; echo $a; unset a
    7
    $ a=5+2; echo $a; unset a
    5+2
    $ declare -i a=5+2; echo $a; unset a
    7



Parameter Expansion

Parameter Expansion is the term that refers to any operation that causes a parameter to be expanded (replaced by content). In its most basic appearance, the parameter expansion of a parameter is achieved by prefixing that parameter with a $ sign. In certain situations, additional curly brackets around the parameter's name are required:

    $ echo "'$USER', '$USERs', '${USER}s'"
    'lhunath', '', 'lhunaths'

This example illustrates what basic parameter expansions look like. The second PE results in an empty string. That's because the parameter USERs is empty. We did not intend to have the s be part of the parameter name. Since there's no way for BASH to determine whether you want the s appended to the name of the parameter or its value you need to use curly brackets to mark the beginning and end of the parameter name. That's what we do in the third PE in our example above.

Parameter Expansion can also be used to modify the string that will be expanded. These operations are terribly convenient:

    $ for file in *.JPG *.jpeg
    > do mv "$file" "${file%.*}.jpg"
    > done

The code above can be used to rename all JPEG files with a JPG or a jpeg extension to have a normal jpg extension. The PE (${file%.*}) cuts off everything from the end until it finds a period (.). Then, in the same quotes, a new extension is appended to the expansion result.

Here's a summary of most PEs that are available:

You will learn them all through experience. They will come in handy far more often than you think they might. Here's a few examples to kickstart you:

    $ file="$HOME/.secrets/007"; \
    > echo "File location: $file"; \
    > echo "Filename: ${file##*/}"; \
    > echo "Directory of file: ${file%/*}"; \
    > echo "Non-secret file: ${file/secrets/not_secret}"; \
    > echo; \
    > echo "Other file location: ${other:-There is no other file}"; \
    > echo "Using file if there is no other file: ${other:=$file}"; \
    > echo "Other filename: ${other##*/}"; \
    > echo "Other file location length: ${#other}"
    File location: /home/lhunath/.secrets/007
    Filename: 007
    Directory of file: /home/lhunath/.secrets
    Non-secret file: /home/lhunath/.not_secret/007
    Other file location: There is no other file
    Using file if there is no other file: /home/lhunath/.secrets/007
    Other filename: 007
    Other file location length: 26

Remember the difference between ${v#p} and ${v##p}. The doubling of the # character means metacharacters will become greedy. The same goes for %:

    $ version=1.5.9; echo "MAJOR: ${version%%.*}, MINOR: ${version#*.}."
    MAJOR: 1, MINOR: 5.9.
    $ echo "Dash: ${version/./-}, Dashes: ${version//./-}."
    Dash: 1-5.9, Dashes: 1-5-9.

Note: You cannot nest PEs together. If you need to execute multiple PEs on a parameter, you will need to use multiple statements:

    $ file=$HOME/image.jpg; file=${file##*/}; echo "${file%.*}"
    image