Shell Script #2 - Variables, Some Special Characters, and Exit Codes
Code examples shared in my GitHub repository: Shell Scripting 101
What is a Variable?
If you're familiar with programming, variables are not an unfamiliar concept. For variables, we can describe them as the "label" or "name" of data stored in memory. We use variables to call certain locations or blocks of memory.
Bash Scripting Variable Examples
In the Bash environment, assigning and calling the values of variables can vary under different conditions. In this section, we'll discuss the forms commonly encountered in general usage.
Variables are called by their names when assigned or changed. However, when retrieving their values, they take the "$" prefix. Let's make some classic examples. I will explain this section step by step:
1. [root@localhost bashscript]# isim =Ali
2. -bash: isim: komut yok
3. [root@localhost bashscript]# isim=Ali
4. [root@localhost bashscript]# soyisim=Sezişli
5. [root@localhost bashscript]# mesaj=Merhaba $isim $soyisim
6. -bash: Ali: komut yok
7. [root@localhost bashscript]# mesaj="Merhaba $isim $soyisim"
8. [root@localhost bashscript]# echo mesaj
9. mesaj
10. [root@localhost bashscript]# echo $mesaj
11. Merhaba Ali Sezişli
12. [root@localhost bashscript]#
- I tried assigning the value "Ali" to the variable "isim".
- The bash program said it couldn't find a command named "isim". This is because I left a space before the equals sign during the assignment. Important note: In the bash environment, you must not leave spaces when assigning variables. Otherwise, bash interprets the values you write as separate commands or arguments to those commands.
- I assigned the value "Ali" to the variable "isim".
- I assigned the value "Sezişli" to the variable "soyisim".
- I'm trying to assign the value "Merhaba $isim $soyisim" to the variable "mesaj". Note that there are no quotation marks. Actually, according to bash, I wrote this: mesaj=Merhaba Ali Sezişli. "What's wrong with that?" you might ask. Well, as I tried to explain above, bash interprets these space-separated words as separate commands.
- It tried to run the value "Ali" from the variable $isim as if it were a program. This failed.
- During the assignment, I enclosed the value in quotation marks to indicate that these are not commands but text.
- I told the "echo" program to write "mesaj".
- It wrote it :) It's very normal that it understood the word "mesaj". To indicate that I meant the variable "mesaj," I should have prefixed it with a $ symbol.
- I told the "echo" program to write the value of the "mesaj" variable.
- We got the expected output.
Let's do some messy stuff:
1. [root@localhost bashscript]# test=42
2. [root@localhost bashscript]# echo test
3. test
4. [root@localhost bashscript]# echo $test
5. 42
6. [root@localhost bashscript]# test=A L İ
7. -bash: L: komut yok
8. [root@localhost bashscript]# test="A L İ"
9. [root@localhost bashscript]# echo $test
10. A L İ
11. [root@localhost bashscript]# echo "$test"
12. A L İ
13. [root@localhost bashscript]# echo '$test'
14. $test
15. [root@localhost bashscript]# degisken1=100 degisken2=200 degisken3=300
16. [root@localhost bashscript]# echo "$degisken1 $degisken2 $degisken3"
17. 100 200 300
18. [root@localhost bashscript]# echo $degisken1 $degisken2 $degisken3
19. 100 200 300
20. [root@localhost bashscript]#
- I assign the value 42 to the "test" variable.
- I print "test" to the screen. It's not the variable "test". I didn't use the $ symbol.
- As expected, "test" is printed.
- This time, I want to print the "test" variable. It has the $ symbol at the beginning.
- As expected, 42 is printed.
- I want to assign new values to the "test" variable. However, there are space characters in between. Assignments do not allow this.
- While attempting the assignment, bash considers the characters after the space as commands. It tries to execute the "L" command and fails.
- I assign a value to the "test" variable using quotation marks, allowing spaces.
- I want to print the "test" variable.
- Only "A", "L", and "İ" characters appear on the screen with single spaces in between.
- I try to print the same variable within double quotes.
- As defined earlier, it is printed with multiple spaces.
- I try to print the "test" variable within single quotes.
- Since the "echo" program expects a string literal within single quotes, it directly prints "$test" characters to the screen.
- In this line, I define and assign values to 3 different variables with a single space between them. This usage is not highly recommended. It can cause issues with code readability and script portability.
- I want to print the values of all 3 variables. I use double quotes.
- As expected, I see the output "100 200 300".
- I send the values of the 3 variables to "echo" without double quotes.
- As expected, I see the output "100 200 300".
Some Special Characters
We touched on some of these in previous writings. In this and future examples, we will encounter different special characters.
If you're familiar with the bash environment, you already know where, when, and for what purpose characters like double quotes ("), single quotes ('), pipe (|), question mark (?), asterisk (*), ampersand (&), semicolon (;) are used. It's not possible for me to examine and explain each character individually. I don't have that knowledge anyway. We will try to make sense of these characters as they appear.
The table below shows the general purpose of these characters. However, characters may behave differently in different contexts. Remember the double quotes example from the first post.
| Character | Feature |
|---|---|
| * | Wildcard for 0, 1, or more characters |
| ? | Wildcard for 1 character |
| ; | Joins different commands on the same line |
| | | Sends the output of the previous command to the next program |
| & | Starts the process in the background |
| || | OR operator |
| && | AND operator |
| #! | Shebang (only if it is at the very top) |
| # | Comment line |
| $ | Represents variables |
| $(...) | Executes the command inside the parentheses and sends its output |
Again, I want to emphasize that these characters do not necessarily work exactly as written above each time. They may have different purposes in different special situations.
A Bash Script Example
Let's try creating a simple script. The expectations from this script will be:
- Greet the user by "name".
- Display today's date.
- Show the number of active users logged into the system.
- Print a calendar on the screen.
- Exit with code 0.
Environment Variables
In GNU/Linux systems, environment variables (env) store values for use by applications. For example, you're going to open a text file. What is your default text editor? Which user is currently using this application? Which directory are we in? In which language is our interface? Aside from these and similar general values, you can also define environment variables yourself and use them.
Since the topic of this article is not environment variables, I won't delve into it. However, I recommend checking out the "env" program.
Exit Codes
In the GNU/Linux environment, you can see the behavior of software by examining exit codes. Most of the time, if a software works correctly, you'll see an exit code of 0 (zero). But keep in mind, the person who determines the exit codes is the developer. Below, you can see the different exit codes of the "ls" program. To see the exit code of your last command, you can use the command "echo $?". These codes can also be called "exit code" or "return code".
[ali@localhost bashscript]$ ls /root
ls: dizin /root açılamadı: Erişim engellendi
[ali@localhost bashscript]$ echo $?
2
[ali@localhost bashscript]$ ls /home/ali
hello.c hello.exe
[ali@localhost bashscript]$ echo $?
0
[ali@localhost bashscript]$ ls -abcdfefgerert
ls: geçersiz seçenek -- e
Try 'ls --help' for more information.
[ali@localhost bashscript]$ echo $?
2
As you can see above, the "ls" program returned code 0 when it worked successfully. However, when access was denied or an incorrect argument was given, it returned an exit code of 2.
Now, let's examine this output:
[ali@localhost ~]$ gcc -o hello.exe hello.c
[ali@localhost ~]$ echo $?
0
[ali@localhost ~]$ ./hello.exe
Hello World!
[ali@localhost ~]$ echo $?
123
[ali@localhost ~]$
A C code has been compiled with the help of "gcc". It exited with code 0. A program named "hello.exe" was created. This program printed "Hello World!" to the screen, moved to the next line, and completed its task. However, the exit code returned was "123". Why? Is it because it didn't work successfully? No. It's because the programmer wanted it that way.
The "123" value that comes after the "return" keyword above shows the value that this program will return when it runs successfully. To my friends taking C classes, when you asked "Why do we always write return 0?" (I hope you asked this); you may have received answers like "Well, it's just written like that" or "Nah, I write it like this, you can write something else if you want." Maybe programmers once agreed among themselves, "Guys, let's not overcomplicate it, if it's successful, return zero, okay?"
Preparing the Script
I will add line numbers to the code in this section to make the explanations easier.
1. #! /bin/bash
2. echo "Merhaba $USER"
3. echo "Bugün: $(date)"
4. echo -e "Aktif kullanıcı sayısı: \c"; who | wc -l
5. echo "Takvimin:"
6. cal
7. exit 0
Let's analyze it:
- Shebang line. I want this script to be interpreted through the "/bin/bash" program.
- I asked to print the word "Merhaba" along with the value of the environment variable "$USER". "$USER" shows the owner of the current session.
- I run the "date" program and send its output to the "echo" program, which is one step outside. We examined this while debugging.
- This is a bit different. If you look at the "man" page for the "echo" program, you'll see that the "\c" expression means "stop outputting." But how can echo know if you really want to write "\c"? That's why we use the "-e" flag for echo. This way, echo understands that it should behave differently when it encounters backslash expressions. So why am I doing this? Why do I want to stop outputting? Because echo, by default, moves to a new line after printing something. But I don't want that. Instead, I want to see the number of active sessions on the system. Here, we connect the "echo" part with the part that calculates the count using the ";" character. Echo printed something, and before moving to a new line, it was stopped. When the semicolon comes, bash understands: "I've already executed up to here, but there's more to come." The "who" program lists the open sessions on the system. Instead of seeing this output, I send it through a pipe (|) to the "wc" (word count) program. The "wc" program shows how many lines, words, and bytes the provided input contains. Since I only wanted to know the number of lines, I used the "-l" flag.
- We printed "Takvimin:" on the screen.
- We ran the "cal" program. The "cal" program shows a calendar in your shell.
- We ended the script with exit code 0.
Let's run it:
[root@localhost bashscript]# ./egzersiz_1.sh
Merhaba root
Bugün: Cum Kas 20 14:08:22 +03 2020
Aktif kullanıcı sayısı: 1
Takvimin:
Kasım 2020
Pz Sa Çr Pr Cu Ct Pa
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
[root@localhost bashscript]# echo $?
0
[root@localhost bashscript]#
I think the output seen on the screen will become clearer if you try to match it with the explanations of the code above.
Actually, even if you write the commands one by one, you will get similar results:
[root@localhost bashscript]# echo "Merhaba $USER"
Merhaba root
[root@localhost bashscript]# echo "Bugün: $(date)"
Bugün: Cum Kas 20 14:10:52 +03 2020
[root@localhost bashscript]# echo -e "Aktif kullanıcı sayısı: \c"; who | wc -l
Aktif kullanıcı sayısı: 1
[root@localhost bashscript]# echo "Takvimin:"
Takvimin:
[root@localhost bashscript]# cal
Kasım 2020
Pz Sa Çr Pr Cu Ct Pa
1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30
[root@localhost bashscript]#
The point of Bash scripting is this. "You could do it yourself, but let the computer do the job for you." It helps when you need to automate tasks that you want to be done on someone else's computer, repetitive tasks, long commands, or tasks that need to be executed sequentially. A well-written bash script can continue performing the tasks on your behalf after you execute it, allowing you to leave your computer to do the job.
As a Result
With Bash scripts, you can automate the tasks you perform in your shell.
When scripting in Bash, you're still using the shell. The special characters and commands you use in the shell can also be used in scripts. Of course, in some special scenarios, things might not go as expected.
Pay attention to space characters when assigning values to variables. Don't overlook the difference between simple, single-quoted, and double-quoted usage when using variable values.
