Bash Script Examples (continued, still very very basic)

in #bash7 years ago (edited)

I'm having some trouble sorting out the beginning of some fiction that I'm working on, so I figured I'd switch gears and write up a continuation of the bash script examples. This got too long, so I'm going to have to continue it yet again in the near future, but for now, have some more bash scripty goodness.

Continuing where Bash Script Examples left off...

Return Values

Every line that executes produces a return value. Generally the return value is a zero for success and a non-zero for failure. The return value of the previous command or function can be used to decide what action to take next. There are a few different ways to leverage this.

Note that I chose to use rm in many examples, when a file doesn't exist, it
will complain on standard error. I don't mention this in the command output
below, but many of the examples will also include the following:

    rm: cannot remove 'file': No such file or directory

&& || if

To execute the next line only when the current line was successful, use &&.

For example:

    rm file && echo file removed

This will attempt to remove a file named file from the current working directory and if it is successful it will then display file removed.

Note that the && operator gives you multi-line commands for free, so that could also be expressed as this:

    rm file &&
    echo "File removed!"

To execute the next line only when the current line fails, use ||.

For example:

    rm file ||
    echo "Failed to remove the file!"

Now, if removing the file fails it will print out Failed to remove the file!

These can be combined, but it can make it hard to read, for example:

    rm file &&
    echo "File removed!" ||
    echo "Failed to remove file"

This will output File removed if it succeeds in removing file or it will print out Failed to remove file if it fails.

The if statement can also be used for conditional execution. For example:

    if rm file ; then
        echo "File removed!"
    else
        echo "Failed to remove file"
    fi

This will print out File removed! if it succeeds in removing file or it will print out Failed to remove file.

The logic can be reversed by using the ! operator:

    if ! rm file ; then
        echo "Failed to remove file"
    else
        echo "File removed!"
    fi

true and false and []

It occurs to me now that maybe I should have used the true built-in instead of removing a file in those examples. Bash has a couple of special, boolean built-ins, namely true and false. These return 0 and 1 as their exit codes, respectively.

For example:

    if true ; then
    echo "True!"
    fi

This will produce the output: True!

And of course there is false:

    if ! false ; then
        echo "False!"
    fi

This will produce the output: False!

There are also string and number comparison operations that an be used in an
if statement by using the square brackets [].

For example:

    if [ 0 -eq 0 ] ; then
        echo "Zero equals Zero!"
    fi
    if [ 0 -lt 1 ] ; then
        echo "Zero is less than one!"
    fi
    if [ 1 -gt 0 ] ; then
        echo "One is greater than zero!"
    fi
    I=2
    if [ ${I} -lt 100 ] ; then
        echo "${I} is less than 100"
    fi
    if [ "one" = "one" ] ; then
        echo "running out of contrived examples!"
    fi

All of which will produce the following:

Zero equals Zero!
Zero is less than one!
One is greater than zero!
2 is less than 100
running out of contrived examples!

Return values from functions

When you wrap a bunch of commands in a function you probably want to pass the error state back to the main script. There are two ways to do this, one is with an explicit return statement.

For example:

    # This function always returns one.
    one(){
        return 1
    }

The other way is to use the error status of the last line to execute.

For example:

    remove_it() {
        rm file
    }

    if remove_it ; then
        echo "File removed!"
    else
        echo "Failed to remove file"
    fi

In this example, the last line of the function is the rm command and it will set the return code even though there is no explicit return in the remove_it function. Like the previous blocks, this will print out File removed! if it succeeds in removing file or it will print out Failed to remove file.

Loops

Bash supports for, while, until, and something called select. But I'm skipping select for now because I don't really feel like it's a loop, it's uncommon, and frankly I write many of these posts while I'm offline and I don't want to go get online just to look up a practical example. So, I'll save that for another time.

for

The for loop can loop over a number range or a set of arbitrary values.

for a range

First, the number range example:

    for ((i=0;i<10;i++)) ; do
        echo "i is ${i}"
    done

This will start ${i} at zero and execute the echo command ten times, displaying the following:

i is 0
i is 1
i is 2
i is 3
i is 4
i is 5
i is 6
i is 7
i is 8
i is 9

for arbitrary values in a list

Now, a range of arbitrary values from a string literal:

    for i in 1 2 3 steem ; do
        echo "i is ${i}"
    done

This will produce the following output:

i is 1
i is 2
i is 3
i is steem

for arbitrary globbing

A very common use of the for loop is to loop over file names in a directory using globbing. I'm not going to go into detail on globbing right now because that would take me on a lengthy detour from loops, but I'll probably cover the topic later. For now, globbing is taking some expression of tokens that have a special meaning and expanding them to match file names.

Here is a for loop that uses a very common directory glob:

    for dir in */ ; do
        echo "Dir: ${dir}"
    done

Assuming there are sub-directories in the current directory, this would print their names.

For example, it may produce something like the following:

Dir: bin/
Dir: boot/
Dir: dev/
...

for array values

This is a take on the loop over arbitrary values where an array is used to supply the arbitrary values.

For example:

    VALUES=("one" "more" "time")
    for VALUE in ${VALUES[@]} ; do
        echo "value: ${VALUE}"
    done

Will produce the following output:

value: one
value: more
value: time

while and until

The while loop executes for as long as the exit status of the test expression is true. It sounds more complex than it is, it behaves like the if statement but repeats its body. Even that sounds complicated, so here's a simple example:

For example:

    while true ; do
        echo "This is the song that never ends..."
    done

This will loop forever, printing out This is the song that never ends...

The until loop is like the while loop, but it runs until its expression evaluates to false. For example:

    until false ; do
        echo "This is the song that never ends..."
    done

This will loop forever, printing out This is the song that never ends...

Summary

Now that I've said how simple it is I'm going to end these examples here so that when I come back to this thread I can post what looks like a horribly complicated while loop, but is really full of awesome.

Here's a summary of what's been covered so far:

  • conditional execution of the next statement using && and ||
  • if statements
  • implicit and explicit function return values
  • for loops over numbers
  • for loops over lists
  • for loops with globbing
  • while loops
  • until loops

Image source is Pixabay

Sort:  

After seeing a discussion in steemit.chat I wanted to go back through and add some sources to my old posts. Since this one is too old to edit, I'm leaving them here in a comment.

The man pages for Bash and all of the other commands I've used above appear to cover 100% the stuff that I put in. If you're not on a Linux or *nix system where you can view man pages, the Bash manual is available online.