Skip to content

Shell Script #1 - (Comments, Here Document, and Debugging)

We wrote our script. We ran it. No issues. Okay...

We wrote our script. We ran it, but it didn't work. Why? Where did we encounter an error? Or. We wrote our script. Three months later, we got an error. We opened it, looked at it. We didn't understand anything. The classic programming problems...

The code examples are shared in my GitHub repository: Shell Scripting 101

Comments

In Bash scripts, we use the "#" symbol for comments. As you remember, shebang also starts with this character. Well, not exactly. Shebang starts with "#!". These two are interpreted differently.

For multi-line comments, we will use "here document". In the script below, you can see the comment lines. Notice that the first line is always the shebang.

#! /bin/bash
#A simple listing script
#While listing, it shows hidden files, permissions, and inode numbers.
#It shows file sizes in "human-readable" format (K, M, G, etc.).

#Display a message to the user:
echo "The contents of the current directory are listed below:"
ls -alhi

Output of our script:

[root@localhost bashscript]# ./yorumlar.sh
Bulunduğunuz dizinin içeriği aşağıda listelenmiştir:
toplam 12K
16797781 drwxr-xr-x. 2 root root   47 Eki 28 18:30 .
16797761 dr-xr-x---. 8 root root 4,0K Eki 28 14:53 ..
16802432 -rwxr--r--. 1 root root  126 Eki 28 15:07 echo_ornegi.sh
16802435 -rwxr--r--. 1 root root  541 Eki 28 18:30 yorumlar.sh
[root@localhost bashscript]#

Here Document

A here document is a block of code used for a special purpose. It acts as a form of I/O redirection. This allows you to feed an interactive command. Let's try to explore this with an example.

With the wc program, we can see the line, word, and size information of a file provided as input. To see only the line count, we can use the "-l" flag.

[root@localhost bashscript]# wc -l yorumlar.sh
17 yorumlar.sh

As seen above, there are 17 lines in the "yorumlar.sh" file. So, what if we want to provide pre-written data from the script as input to a program? This is where "here document" comes to our rescue. Let's examine the following example script:

#! /bin/bash

wc -l <<girdi
Test
Örnek veri
Deneme
girdi

We sent the content defined within the "input (girdi)" block to the "wc -l" program. Let's think about this in the context of "mail". We can use this technique to directly attach text prepared in the script to the body of an email.

[root@localhost bashscript]# cat word_count.sh
#! /bin/bash

wc -l <<girdi
Test
Örnek veri
Deneme
girdi
[root@localhost bashscript]# ./word_count.sh
3
[root@localhost bashscript]#

Similarly, if we send this block to a large empty space, we can essentially create a multi-line comment.

[root@localhost bashscript]# cat yorumlar.sh
#! /bin/bash
#Basit bir listeleme script'i
#Listeleme işini yaparken gizli dosyaları, izinleri ve inode numaralarını gösterir.
#Dosya boyutlarını "human-readable" formatta (K, M, G gibi) gösterir.

<<yorumlar
 Bu blokta kalan içerik, tamamen yorum olarak kalacaktır.
 Bu sayede birden fazla satıra yayılmış yorumlar oluşturabilirsiniz.
 Bunu yaparken her satırın başına # eklemenize de gerek olmaz.
yorumlar

#Kullanıcıya bir mesaj göster:
echo "Bulunduğunuz dizinin içeriği aşağıda listelenmiştir:"
ls -alhi

[root@localhost bashscript]# ./yorumlar.sh
Bulunduğunuz dizinin içeriği aşağıda listelenmiştir:
toplam 24K
16797781 drwxr-xr-x. 2 root root   88 Eki 28 23:53 .
16797761 dr-xr-x---. 8 root root 4,0K Eki 28 14:53 ..
17514307 -rw-r--r--. 1 root root    2 Eki 28 22:33 a
16821756 -rw-r--r--. 1 root root    5 Eki 28 22:28 ali
16802432 -rwxr--r--. 1 root root  126 Eki 28 15:07 echo_ornegi.sh
16802431 -rwxr--r--. 1 root root   58 Eki 28 23:53 word_count.sh
16802435 -rwxr--r--. 1 root root  541 Eki 28 18:30 yorumlar.sh
[root@localhost bashscript]#

Debugging in Scripts

Let me share 3 options from the Bash program's man page first. Then, we'll discuss what they do.

-n: Read commands but do not execute them. This may be used to check a shell script for syntax errors. This is ignored by interactive shells. -v: Print shell input lines as they are read. -x: After expanding each simple command, for command, case command, select command, or arithmetic for command, display the expanded value of PS4, followed by the command and its expanded arguments or associated word list.

Let's start.

[root@localhost bashscript]# ls
a  debug.sh  echo_ornegi.sh  word_count.sh  yorumlar.sh
[root@localhost bashscript]# cat debug.sh
#! /bin/bash

echo "Ben bu dosyayı silecek miyim sence?"
rm a
[root@localhost bashscript]# bash -n debug.sh
[root@localhost bashscript]# ls
a  debug.sh  echo_ornegi.sh  word_count.sh  yorumlar.sh
[root@localhost bashscript]#

Let's examine the above example step by step:

  1. I listed the directory with ls. There is a file named "a".
  2. I read the "debug.sh" script using cat. This script attempts to print a message to the screen and delete the "a" file.
  3. Using bash -n ./debug.sh, I gave the script to the bash program in the current directory. With the -n flag, I told bash to just read the commands without executing them.
  4. When I checked again with ls, I saw that the "a" file was not deleted. Moreover, no message was printed on the screen.

Now, let's make the script faulty and try again.

[root@localhost bashscript]# cat debug.sh
#! /bin/bash

ehco "Ben bu dosyayı silecek miyim sence?
remove a
[root@localhost bashscript]# bash -n ./debug.sh
./debug.sh: line 3: `"' için eşleşme aranırken beklenmedik dosya sonu
./debug.sh: line 5: sözdizimi hatası: beklenmeyen dosya sonu
[root@localhost bashscript]#
  1. I read my script with cat. The word "echo" was written as "ehco" and "rm" as "remove". There are typos in this script.
  2. I checked the script with bash -n.
  3. Errors were shown as output. Let's assume that I did a long task. If the script had failed somewhere after many steps were completed correctly, things could have gotten complicated. Fortunately, with bash -n, we were able to see that the script wouldn't work before even starting the job.

Now, let's move on to another example:

[root@localhost bashscript]# ls
a  debug.sh  echo_ornegi.sh  word_count.sh  yorumlar.sh
[root@localhost bashscript]# cat debug.sh
#! /bin/bash

echo "Ben bu dosyayı silecek miyim sence?"
rm a
[root@localhost bashscript]# bash -x ./debug.sh
+ echo 'Ben bu dosyayı silecek miyim sence?'
Ben bu dosyayı silecek miyim sence?
+ rm a
[root@localhost bashscript]# ls
debug.sh  echo_ornegi.sh  word_count.sh  yorumlar.sh
[root@localhost bashscript]#
  1. I checked with ls, and the "a" file is there.
  2. I read it with cat. There doesn't seem to be any typos.
  3. I used the xtrace feature to have the script show "what it understood from what I wrote." Each line was first shown to us, then executed.
  4. After checking with ls again, I saw that the script had deleted the "a" file.

"...what it understood from what I wrote..." Doesn't this sound a bit vague? Here's another example:

[root@localhost bashscript]# ls
a  debug.sh  echo_ornegi.sh  word_count.sh  yorumlar.sh
[root@localhost bashscript]# cat debug.sh
#! /bin/bash

echo "Ben bu dosyayı silecek miyim sence?"
echo "Bu işlemin yapıldığı tarih: $(date)"
rm a
[root@localhost bashscript]# bash -x ./debug.sh
+ echo 'Ben bu dosyayı silecek miyim sence?'
Ben bu dosyayı silecek miyim sence?
++ date
+ echo 'Bu işlemin yapıldığı tarih: Sal Kas  3 16:50:02 +03 2020'
Bu işlemin yapıldığı tarih: Sal Kas  3 16:50:02 +03 2020
+ rm a
[root@localhost bashscript]# ls
debug.sh  echo_ornegi.sh  word_count.sh  yorumlar.sh
[root@localhost bashscript]#
1. I checked with ls. I saw the "a" file. 2. I read it with cat. In the script, "echo" and "date" are nested together in some place. "What I wrote" was an echo with a date. But what does "it understand"? 3. I ran the script with bash -x. 4. It understood the first echo as it is. It showed it, ran it, and we saw the result. 5. In the ++date line, we see that it first said, "I need to run the date command to get the date," then it placed the output of date into the echo and ran it. 6. We can also confirm by checking with ls that the file was deleted.

Is this enough? No:

[root@localhost bashscript]# ls
a  debug.sh  echo_ornegi.sh  word_count.sh  yorumlar.sh
[root@localhost bashscript]# bash -v ./debug.sh
#! /bin/bash

echo "Ben bu dosyayı silecek miyim sence?"
Ben bu dosyayı silecek miyim sence?
echo "Bu işlemin yapıldığı tarih: $(date)"
Bu işlemin yapıldığı tarih: Sal Kas  3 16:54:09 +03 2020
rm a
[root@localhost bashscript]#
  1. I checked with ls. I saw the "a" file.
  2. I wanted the relevant line to be shown to me during script execution with bash -v.
  3. During the execution, I saw that the code of my script was displayed before it was executed. This way, we can see which line produced which output.

Can these tools work together? Of course. To explain better, I'll number this part:

1- [root@localhost bashscript]# bash -xv debug.sh
2- #! /bin/bash

3- echo "Ben bu dosyayı silecek miyim sence?"
4- + echo 'Ben bu dosyayı silecek miyim sence?'
5- Ben bu dosyayı silecek miyim sence?
6- echo "Bu işlemin yapıldığı tarih: $(date)"
7- ++ date
8- + echo 'Bu işlemin yapıldığı tarih: Sal Kas  3 16:56:40 +03 2020'
9- Bu işlemin yapıldığı tarih: Sal Kas  3 16:56:40 +03 2020
10- rm a
11- + rm a
[root@localhost bashscript]#

With bash -xv, I requested both xtrace and verbose. What did I write, what did you understand, and what did you execute?

  • Number 2: Shebang.
  • Number 3: The echo I wrote.
  • Number 4: The version with a +, which is the form understood by bash.
  • Number 5: The output of the above echo.
  • Number 6: The echo I wrote.
  • Number 7: The line starting with ++, where bash said "first I need to run date".
  • Number 8: After date ran, the new form of the echo. I didn't write this, bash turned it into this.
  • Number 9: The output of the echo that bash transformed from my intended form.
  • Number 10: The rm I wrote.
  • Number 11: The line starting with +, which is the form bash understood.

Conclusion

Shell scripts, especially when run with root privileges, can have catastrophic effects. However, they can also allow you to create incredibly powerful tools for automating repetitive tasks. Therefore, you must ensure that your scripts will work in any environment and under any circumstances.

Even if you cannot ensure this, you still have the chance to perform the necessary checks for your script to work.