1
0
Fork 0
mirror of https://codeberg.org/Mo8it/How_To_Linux.git synced 2025-04-11 04:48:37 +00:00
How_To_Linux/src/day_2/shell_scripting.md
2023-08-13 22:04:22 +02:00

9.9 KiB

Shell scripting

Task automation requires multiple instructions that have to run on demand. To combine multiple instructions, we need to write a shell script.

Since bash is the default shell on most Linux distributions, we learn bash scripting.

First bash script

Let's write our first Bash script:

#!/usr/bin/bash

echo "What is your favorite operating system after reading this book?"
echo "1. Linux"
echo "2. Windows"
echo "3. Mac"

RIGHT_ANSWER=1

# The option -n does not print a new line at the end
echo -n "Enter a number: "
read ANSWER

if [ $ANSWER == $RIGHT_ANSWER ]
then
    echo "Good choice!"
else
    # Any answer other than 1
    echo "Nah, that can't be right! It must be an error!" 1>&2
fi

Copy this code into a file called which-os.sh.

Now run chmod u+x which-os.sh. Then run ./which-os.sh. Don't worry, everything will be explained afterwards.

After running the script, you will see a prompt asking you to enter a number corresponding to an operating system. If you choose Linux, you get the output "Good choice". This output is in the standard output.

If you don't choose Linux, you get the output "Nah, that can't be right! (...)". This output is redirected to the standard error.

We did learn that we can redirect to a file using >. The syntax 1>&2 redirects the standard output to the standard error. On the other hand. 2>&1 redirects the standard error to the standard output.

If you want to redirect both the standard output and error to a file, you can use this syntax: COMMAND > FILE 2>&1.

The redirection order is important in this case! COMMAND 2>&1 > FILE will redirect the standard output only to the file FILE!

COMMAND > FILE 2>&1 has a useful shortcut: COMMAND &> FILE. You can also use &>> to append.

All other aspects of the script above will be explained in the next sections.

Shebang

The first line of our first script starts with #! which is called the shebang. The shebang is followed by the program that runs the script. Since the script is a bash script, we use the program bash to run it. But writing bash after the shebang is not enough. We have to specify the path to the program. We can find out the path of the program by using the command which:

$ which bash
/usr/bin/bash

You can also write a Python script and add a shebang at its beginning. We can find out the path to the Python program:

$ which python3
/usr/bin/python3

This means that we can now write this script:

#!/usr/bin/python3

print("Hello world!")

Let's save this tiny Python script as hello_world.py, make it executable with chmod (will be explained later) and then run it:

$ chmod u+x hello_world.py
$ ./hello_world.py
Hello world!

We could have written the Python script without the shebang, but then we would have to run with python3 hello_world.py. Adding the shebang lets you see a script as a program and ignore what language it is written in when running it.

You can use the shebang with any program that runs a script (interpreter).

Variables

In our first bash script, we have the line RIGHT_ANSWER=1. This line defines a variable with the name RIGHT_ANSWER and the value 1.

To define a variable in bash, you have to write the name of the variable directly followed by an equal sign =. The equal sign has to be directly followed by the value of the variable.

directly followed means that spaces between the variable name, the equal sign = and the value are not allowed!

But what if you want to set a variable equal to a string that contains spaces?

In this case, you have to use quotation marks ". For example:

HELLO="Hello world!"

To use a defined variable, we use a dollar sign $ before the variable name. For example:

echo $HELLO

The line above would output Hello world!.

You can use defined variable inside a variable definition:

MESSAGE="Tux says: $HELLO"
echo $MESSAGE

The two lines above would lead to the output Tux says: Hello world!.

Capturing command's output

You can capture the standard output of a command using the the syntax $(COMMAND). Example:

BASH_VERSION=$(bash --version)

The line above saves the output of the command bash --version in the variable BASH_VERSION.

Let's run the command in the terminal first to see its output:

$ bash --version
GNU bash, version 5.1.16(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Now, let's output the variable that we did define above:

$ echo $BASH_VERSION
GNU bash, version 5.1.16(1)-release (x86_64-redhat-linux-gnu) Copyright (C) 2020 Free Software Foundation, Inc. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> This is free software; you are free to change and redistribute it. There is NO WARRANTY, to the extent permitted by law.

You can see that the lines are squashed into one line! If you want to output the lines without them being squashed, you have to use quotation marks ":

$ echo "$BASH_VERSION"
GNU bash, version 5.1.16(1)-release (x86_64-redhat-linux-gnu)
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

This is the output that we did expect 😃

Temporary variables

Let's write the following tiny script hello.sh:

#!/usr/bin/bash

echo "Hello $USER!"

Now we run the script. The output on my machine is:

$ chmod u+x hello.sh
$ ./hello.sh
Hello mo!

We see Hello mo! as output. This is because my user name on my machine is mo. USER is a so called environment variable that is defined for all programs. If you run the script on your machine, you will get your user name instead of mo. There are more environment variables like PATH which we will learn about later.

Now, let's run the following:

$ USER=Tux ./hello.sh
Hello Tux!

We did define a temporary variable USER with the value Tux. This temporary variable overwrites the environment variable USER in our script. Therefore we get the output Hello Tux! and not our user name.

You can use temporary variables not only to overwrite environment variables. These are basically variables that can be used by the program that you specify after their definition.

Comments

In our first bash script, you can find three lines starting with a hashtag #. Two lines are comments, the shebang #! is an exception as a special comment at the beginning of a file that is not ignored while running the script. All other lines that start with # are comments that are ignored by the computer. Comments are only for humans to explain things.

Especially in long scripts, you should write comments to explain what the script and its "non trivial sections" do.

User input

In a script, you can ask for user input. To do so, you can use the command read.

In our first bash script, we use read to take the answer of the user. The input is then saved in the variable ANSWER. You can choose a different name for this variable. After the line with read, you can use the variable storing the input as a normal variable.

Conditions

if block

Our first bash script checks if the user input which is stored in the variable ANSWER equals the variable RIGHT_ANSWER which stores the value 1.

To check for a condition in Bash, we use the following syntax:

if [ CONDITION ]
then
    (...)
fi

Here, (...) stands for the commands that we want to run if the condition is true.

In our first bash script, we check for equality of two variables with a double equal sign ==.

fi is not a typo! It is just if reversed to indicate the end of the if block. Although the syntax is not the best, you have to sadly accept it. Bash does not have the best syntax...

Speaking about syntax: You have to take spaces seriously with conditions.

For example, if we define the variable VAR=1, the following snippets do not work (or have an unexpected behavior):

  1. No space after [

    if [$VAR == 1 ]
    then
        echo "VAR has the value 1"
    fi
    
  2. No space before ]

    if [ $VAR == 1]
    then
        echo "VAR has the value 1"
    fi
    
  3. No space before == but a space after ==

    if [ $VAR== 1 ]
    then
        echo "VAR has the value 1"
    fi
    
  4. No space after == but a space before ==

    if [ $VAR ==1 ]
    then
        echo "VAR has the value 1"
    fi
    
  5. No space before == and after ==

    if [ $VAR==1 ]
    then
        echo "VAR has the value 1"
    fi
    

But the following snippet work:

  • Space after [, before ], before == and after ==
    if [ $VAR == 1 ]
    then
         echo "VAR has the value 1"
    fi
    

else block

The else block runs commands inside it only if the condition is not true. The syntax is:

if [ CONDITION ]
then
    # Runs only if CONDITION is true
    (...)
else
    # Runs only if CONDITION is false
    (...)
fi

Example:

if [ $VAR == 1 ]
then
    echo "VAR has the value 1"
else
    echo "VAR does not have the value 1"
fi

Rest coming soon...