One elephant ran a script in bash…

This article has been originally published in Italian (here). Feedbacks on content and translation are appreciated. Contributions are welcome. The original article must be considered the reference in case of updates.

konsole_48Let the commonplaces about Linux begin: “Linux is for coders”, “It’s all command line”, “It’s difficult to use”, “The terminal!”.

Ok, enough for the well-worn list of set phrases, let’s write a simple program to be run in a terminal.

Ehm 😳 you know… that matter of terminal, Linux, ehm.. I mean… just let me clarify…

Computers are incredibly fast, accurate, and stupid. Human beings are incredibly slow, inaccurate, and brilliant. Together they are powerful beyond imagination.

A. Einstein

Cirque du Soleil Istanbul 2012 Alegria 1200660 nevit

“Flexibility” (Source: Wikimedia Commons)

Except for some special cases involving human beings, ol’ uncle Albert is right.

We only need to learn how to order these dumb buzzing boxes to carry out mundane and recurring tasks we can’t be bothered with.

But why should it be worth the hassle to learn obscure codes, named “scripts” by adepts, when so many nice graphical programs exist, with their colourful windows and buttons?

Flexibility, that’s why.

In many cases, also, windows & buttons are nothing but a visual “aid” to command programs that might even get along without them.

Learning few simple “rules” we might be able to put together scripts (or simpler list of commands) that fulfil specific needs, combining tools that are usually already available on any Linux distribution.

Here come the elephants!

Sort of. Actually I will “show & tell” a simple example script, that performs an interesting and fun task: it “sings” a kids song! The script has been devised specifically to show some of the most useful and commonly used available functions.

If you want to try it, follow these simple steps:

  1. copy the whole text in an empty file and save it, for example with name “
  2. make it executable with chmod +x
  3. run it in a terminal with ./ (from folder where you saved it in) or
    ./fullpath/toscript/ from any other folder.
# Sing along with me!
secondrow="On a spider web one day" # A "variable" for text...
num=0 # more for a number.
for i in One Two Three Four Five Six Seven Eight Nine Ten # For each element in this list...
num=$(($num+1))     # A little maths
if [ "$i" = "One" ] # If there's only one elephant
then                # then (!)
eleph="t"           # Use the right words
o="he"              # in the singular
else                # otherwise
eleph="ts"          # use words
o="they"            # in the plural.
fi                  # No more conditions.
echo $i! \($num\) | tr [:lower:] [:upper:]           # What the "pipe" is this?!
echo $i elephan$eleph went out to play               # Write,...
echo $secondrow                                      # ...write a little more,...
echo It was such enormous fun                        # ...and then again,...
echo -e That $o called another elephant to come.\\n  # ...that's enough.
# Pace for test runs
sleep 1 # Take a 1-second nap!
# Normal pace
#sleep 15
exit # Go out and play, you kiddie!

Here the result (summarized) to see what this bunch of  meaningless words can do:

ONE! (1)
One elephant went out to play
On a spider web one day
It was such enormous fun
That he called another elephant to come.

TEN! (10)
Ten elephants went out to play 
On a spider web one day
It was such enormous fun
That they called another elephant to come.

Well, it seems this weird stuff really can “sing” the elephants song, even guessing the right words. Now let’s try to understand in detail how the whole thingamabob works.

But, please!, don’t sing it this way to your kids! 🙂

How elephants work

Row 1: First of all we notify the operative system that instructions in the script have to be interpreted (executed) by bash shell rather than by another of the available shells. Shell is, basically, the notorious terminal and bash is the most used shell. On my pc available shells are two: bash and dash (other ones are links to actual shells). Busybox is what you see when something is pretty f**cked up, so hope you never have to deal with it. 🙂

$ cat /etc/shells
# /etc/shells: valid login shells
$ ls -l /bin/*sh*
-rwxr-xr-x 1 root root 920796 set 19  2012 /bin/bash
-rwxr-xr-x 1 root root  96232 ago 15  2012 /bin/dash
lrwxrwxrwx 1 root root      4 dic 28 02:36 /bin/rbash -> bash
lrwxrwxrwx 1 root root      4 dic 28 02:36 /bin/sh -> dash
lrwxrwxrwx 1 root root      4 dic 28 02:36 /bin/sh.distrib -> dash
lrwxrwxrwx 1 root root      7 nov 16 18:58 /bin/static-sh -> busybox

The declaration of the shell to be used is always placed in the first row, using the special combination “#!” followed by the full path to shell. When the script contains only a list of commands (for example execution of system updates) such declaration may be omitted. Here, instead, since script also contains some “structures” to control the execution, the declaration is necessary because there might be differences in commands syntax among different shells.

Row 2: Differently from previous “#!“, the only “#” (hash) character prefixes a comment. Comments are ignored in script execution but are nonetheless useful to add details: what the script does, who created it, possible improvements from previous versions and so on. Comments can be placed on “dedicated” rows as well as “in line” with commands, as shown. The rule is always valid: anything preceded by “#” is not considered an instruction to execute.

For “educational” purposes I have a little overused comments to document how the script works: actually you can understand what the script does in each row without even looking at the “real” code.

Rows 3-4: In each programming language using “variables” is fundamental. Those are “boxes” in which data used in program execution is stored. To assign a value to a variable syntax is variablename=value. To use the value in a script, that is to operate on the value, it is necessary to prefix the variable name with “$“.

Rows 5-27: One of the most used “cycles” is the “for” cycle. This cycle executes a group of instructions on a list of values, assigned at each iteration to the control variable.  Here variable “i” will hold each time a different value from the list. Values list can be created in different ways, making this cycle very flexible. For example:

  • all files by type: for f in *.txt
  • all files in a folder: for g in `ls /full/path`
  • a range of values (for example 1 to 10): for t in {1..10}

Once the list is defined, all actions included between the opening do (row 6) and the final done (row 27) will be executed.

Row 7: Shell can even do some maths, although only on integers. Syntax for operations is $((<operation>))

Instruction on this line tells the script: increase by 1 the current value of num (thus the “$” is needed) and assign the new value to the same variable.

Rows 8-17: Another classic and essential clause: if... then... else... fi. That is IF the indicated condition is verified THEN do some things OTHERWISE do other things. The closing “fi” (“if” reversed!) is a delimiter for the instructions block, similar to do... done in for cycle.

On the first “round” defined by for cycle (variable “i” having value “One”) I need to have all words in the singular, then all words in the plural: this check allows the script to “realize” the change and assign the proper values to relevant variables (word suffixes).

Rows 18-22: With the right suffixes put in place, we can now view the text with instruction echo. It can print out either an explicitly written string or the content of a variable; both examples are shown in the script.

Row 17: Let me step back a few lines to explain the so called command “piping“. Basically a number of commands can be “connected” by “pipes” (hence the “|” character name!) that let the output of one command be become the input of the next on in the sequence. The “|” character itself is everything you need for that, no voodoo-based syntax:

command1 | command2 | ... | commandN

This way partial results of one command can be further processed to obtain the desired final result. In the script I am converting the whole text line held in $i, coming from previous command echo, in CAPITAL letters trough tr command: so One -> ONE; Two -> TWO; …); $num  is not affected by case conversion since it is a numeric value.

Row 22: Forward again to the last line of code block. I will now reveal the “mystery of disappeared characters”. In row 17 there are a few backslash “\” characters that do not appear in the script printout.

Backslashes indicate that the character following them must be interpreted literally although it might have a special function (special characters such as “!” or “:” can alter the execution of code): this is called character “escaping“.

An example of “escaping” is given by parentheses “(” “)” in row 17 that are normally meant to enclose a list of values. If parentheses had not been escaped, shell would have looked for instructions or data that were consistent with those special characters function; obviously it finds nothing like that, gets “dazed and confused” and stops code execution. When echo has to process a special sequence rather than a single character, option “-e” has to be added (where “-e” stands, you’ll guess, for “escape”). Special sequence is “\n“, representing a new line, preceded (escaped! :-)) by “\“. Another way to print special characters literally is to enclose them in single (‘) or double (“) quotes.

Rows 23-26: Time to relax… Instruction sleep is meant exactly for the purpose to let execution sleep (or wait, if you prefer) for a given time: 1 or 15 seconds, being the time elapsed between one “strophe” and the next one.

Instruction “sleep 15” (row 26) shows another way to use comments, or better still the “trick” to turn instructions we want to exclude from execution into comments, without actually deleting them from the script. Alternative code blocks can therefore be present, in order to test the best solution or to keep a “place holder” for draft or wrong code awaiting further improvements.

Row 28: Instruction exit closes the script: anything below this line will not be executed. An exit can be added at any point to terminate the script when given conditions are met (e.g. inside an if cycle).

How to make an elephant (and how to translate it)

Don’t even think you can get away before reading tons of stuff I am suggesting on the topic (and there’s a lot more…)! 🙂 First head to man pages (starting from man bash) of cited commands. Short after that here is the “classic” bulky book: the Advanced Bash Scripting Guide (author’s web site) is THE reference for scripting. It is a very clear and exhaustive guide, full of documented examples. You just can’t miss it.

Another nice source of information and “ready to use” solutions is Stackoverflow, “a question and answer site for professional and enthusiast programmers“. Average level of forum users is quite high, and so is the quality of answers you can get or find.

If, indeed, you’re more a social kind, take a look at @climagic on Twitter, that posts about creative and clever uses of command line (there are videos in its YouTube channel as well). More Twitter accounts on the same topic can be found but, y’ know, I am not much of a tweeter so you’ll have to find them by yourself. I can swear they are there, really, I’ve seen them.

A final note on this post: actually the hardest part was to find the English version of the elephants song! Of course I had to find the same song, unless I could afford to write the script and the whole post from scratch. Finding this page was harder than I thought… Adapting the script was, conversely, the easiest part: basically, once comments had been translated the job was done.

And that’s all, folks!

if [ "$post" = "finished" ]
exit # That's enough!!!
exit # I mean it...
exit # Anyway...


Informazioni su Man from Mars

Comments are disabled. Please comment and share on Twitter, G+, Facebook.

Inserisci i tuoi dati qui sotto o clicca su un'icona per effettuare l'accesso:


Stai commentando usando il tuo account Chiudi sessione / Modifica )

Foto Twitter

Stai commentando usando il tuo account Twitter. Chiudi sessione / Modifica )

Foto di Facebook

Stai commentando usando il tuo account Facebook. Chiudi sessione / Modifica )

Google+ photo

Stai commentando usando il tuo account Google+. Chiudi sessione / Modifica )

Connessione a %s...

%d blogger hanno fatto clic su Mi Piace per questo: