Loading...


bookmark - Should I “bash” Or “bash”? Or writing scripts in Bourne Again Shell

Should I “bash” Or “bash”? - Or writing scripts in Bourne Again Shell

 
 Discussion by Giniu with 2 Replies.
 Last Update: November 13, 2010, 6:51 pm
 
bookmark - Should I “bash” Or “bash”? Or writing scripts in Bourne Again Shell  
Quickly Post to Should I “bash” Or “bash”? Or writing scripts in Bourne Again Shell w/o signup Share Info about Should I “bash” Or “bash”? Or writing scripts in Bourne Again Shell using Facebook, Twitter etc. email your friend about Should I “bash” Or “bash”? Or writing scripts in Bourne Again Shell Print
Reply / Comment New Discussion / Topic Share / Bookmark E-Mail a Friend Print

Should I “Bash” or “bash”?
Or: “Writing scripts in GNU's Bourne Again Shell”.


For the begining – short story and what is what...

In this tutorial I assume, that you don't have any basic knowledge, but you know Linux (or your other UNIX system) to understand what I'm talking about...

Bash stands for Bourne Again Shell, it is child of Bourne Shell (sh), that was improved with elements of Korn Shell (ksh) and C Shell (csh) to gain power and new possibilities in programming and interactive use. It is quite easy while powerful... but what it is?

What is shell? It is command line interpreter... and what is it? It is something in which you put code to execute... in UNIX command line interpreter is for example sh, bash, ksh, csh and many more... approximately about 30 more, in Windows command line interpreter is MS-DOS... we can say that... but it is quite primitive... anyway... So you knew what shell is, but now you also know that it is called shell... But what the heck is shell script? You heard many times that people was talking... “You can write shell script that would do it for you...” What the... ? So... Let me explain – When you type commands in shell in interactive mode you give them one by one – seeing result after every command... so imagine that you have to do something twice... oh man borring... and when you have to repeat twenty command for hundred files in different directories? Mission impossible... so this is place when you have to write script – a file containing commands you would normally execute one by one, and when you run it, each command is run in right order so you can go get some coffee and relax while shell is doing hard job for you. Popular script file extensions are .sh (for UNIX-like systems, but you mainly don't need to give them any name – you can just make them executable and everything works just like it should) and .bat (for MS-DOS), so you probably saw few shell scripts in your life, but now you know the name...

In this tutorial I wouldn't talk about MS-DOS' scripts, so from now on suppose only info about bash...


Before I start – getting ready place to work...

First of all, unpack your computer from box, look for some place on desk to put it on, then see if you have all wires... :P but serious – I want to talk about getting ready place to work... but on hard drive not on desk – scripts are files like others so you have to get some disk space where you write and test them... There are many schools – some people say, that best place for scripts is /bin directory... others – that it’s /usr/bin or /usr/local/bin... I think best place for scripts is /usr/scripts – that you would create – with symbolic links to /usr/local/bin so you can have all your scripts in one place and you can run them like they would be just in /usr/local/bin or even /bin – this is where I have them, and writing this tutorial I also assume that you would put them there, but of course you can modify this path and everything would work also great – this is you choice... If you decide to choose my way, create script directory:

CODE

mkdir /usr/script

I assume you know about permissions and choose them right – this is not place for this – If you want to know more about permissions refer to “Demystifying "chmod" - Part 1, & why viruses get so retarded on Linux” by microscopic^earthling.

Let the game begin – very basics of bash scripting...

So first of all – you have to know about variables. They are used exactly to what their name describes... they are used to store data... To use some variable you have to assign value to it... you just turned on your computer... you think that you haven't any variable in system? Then type in shell:

CODE

echo $SHELL

you should see

CODE

/bin/bash

if not – execute /bin/bash and repeat this... Well so – there is one variable... maybe there are others? Type:

CODE

echo $LONGNAME
echo $PWD

and how is that? Yes – there are many more... you can see what variables are set by typing echo $ and pressing Tab key twice. So – now let's set some variable...:

CODE

export MYVAR=”My first variable”

then type:

CODE

echo MYVAR

and you should see... MYVAR? Yes... because you don't have to invoke text MYVAR, but refer to value stored in MYVAR... now type:

CODE

echo $MYVAR

uff... now it is working :P So now you see how can variables be used... in Scripts declaration are more easy, you have only to write:

CODE

MYVAR=”My frist variable”

But usage is same, you must refer to it by adding $ at the beginning... So Let's write some easy and useless script... Create text file in /usr/script of desired name ending with .sh, then make it executable, then edit it with your favourite text editor and put into it something like this (don't worry you don't know everything yet... I would explain it in a moment, just remember to put only empty line at end of file):

CODE

#!/bin/bash

# Now set some variable...:
MYVAR=”my first script”
ANOTHERONE=$MYVAR ':)'
# and tell whole world about it, let's there be echo from this !!!
echo this is $MYVAR.

so... as you see this is useless program, but I will now explain it... first line... this strange #!/bin/bash... let's say that your default shell is sh or ksh – so scripts in bash shouldn't work for you? This line tells system “this script is written in bash, execute it in bash interpreter – run /bin/bash”. So – this is now clean what this mean... Then I left empty line – for scripts there doesn't mater how many empty lines you put into script... They are just for easy of read. Next one is strange again... I used hash like in first line, but you see that it must do something differ – and you are right – this is a comment – it also doesn't mater how many comments you put into your script... just when you read it after some time you know what do what... there is other comment in this example... I used in it echo – but it isn't consider as command echo – everything after “#” is ignored... Next line – I set variable... as you see I don't have to use export... you must be warn, that variables that are set in script doesn't exist without it – so you cannot use script to set system variables. But in next line you see, you can use variables to set variables – I used there single quotation to add smile to it... I used it because “()” are registered and reserved to gather code, so when you use it, it thinks you want to do is and not write ) - so I quoted it – they wouldn't be visible. But how can you quote ' ? Just double quote it “ ' “ and to quote “ type ' “ ' - easy, right? At last line I send text to default out – terminal (shell) and as you can see you can use variables and strings (text) in one line without any troubles... also adding dot at end doesn't matter – MYVAR is still interpreted as “MYVAR”, not as “MYVAR.”. And this is all... save it and run it... you should get:

this is my first script :P

So I'm keep going – expresions and I/O redirections...

Right... so now let's make some arithmetics – now not in script but in shell, just like last time... Let's say sum 2 and 5... it should be 7 if I'm not wrong... type:

CODE

expr 2 + 5

as you can see + is divided from 2 and 5 by spaces... and the result is... who knows... 7... :P In bash you also can do multiplications /* (* cannot be used since it used as wildcard in file names), subtraction -, you can divide numbers / (integer results, floats result will be discussed later), count rest while dividing %. But there is one problem... you cannot send it to variable by typing:

CODE

export SUM=expr 2 + 5

since you would get error – also typing

CODE

export SUM=”expr 2 + 5”

or

CODE

export SUM='expr 2 + 5'

won't work since you would get “expr 2 + 5” not “7”... so if single and double quotation doesn't work what can be done? There is way – use backquote “ ` “... try this:

CODE

export SUM=`expr 2 + 5'
echo $SUM

and who knows... we get result! So there is explanation – using backquote sends result to variable... This can be assigned straight to variable, but if you want to use it without assigning result to variable, use $( command ) command, like:

CODE

echo $( expr 2 + 5 )

but it doesn't take care for new lines, try this:

CODE

echo `ls`

as you can see this is usesfull for short results processing. But what if you wan't to process some long text? Like files list above? You have to do some operations on it and as you know – you can make operations on files... so you can send output not ditectly to shell but to file... this works for any command... just use:

CODE

command >> file

to send command results to file, or

CODE

command << file

to send file to command using >> or << would add new strings at end of file, using < or > would overwrite file (and create new if there isn't one)... you can also send command results stright to command in two ways just like they would be usual files. Let's see some example that shows above two things... Let's write script that counts factorial from 9 to11 and send results to file. We would make it better when I would go to loops... But now let's get to work:

CODE

#!/bin/bash

#let's get first 9!
FAC=`expr 2 \* 3 \* 4 \* 5 \* 6 \* 7 \* 8 \* 9`
# send it to file creating new one
echo 9! = $FAC > factorial.result
#count 10!
FAC=`expr $FAC \* 10`
#append it to file
echo 10! = $FAC >> factorial.result
#do same as above for 11!
FAC=`expr $FAC \* 11`
echo 11! = $FAC >> factorial.result

when you would execute this script in directory where you executed it you have now file “factorial.result” that contains desired results... quick and easy... now when you want to count factorial, just see what result it is...

Something more – results processing...

On UNIX-like systems there are many utilities that can be used to scripting, I won't talk about commands like cp, ls, rm, mv, chmod, su or cat since I think they was covered in prerequisites of this tutorial – this is very basic UNIX commands and without them you almost cannot use system... But there are few utilities about which I would write. First of them is wc:

wc comes from “word count” and it counts lines, words and characters in file... the result is formatted like:

lines words characters file

and can be invoked using

CODE

wc file

second utility I would mention is sort – it names says all, it sorts file, sending result to default stream (terminal) so you can redirect it to do example file. You can Invoke it by typing:

CODE

sort file

while you want to sort number, you probably want to use:

CODE

sort -n file

to make 1000 larger that 2, you can also sort in inverse order, by typing:

CODE

sort -r file

Now what if you want to change something in file? There is sed... Most common use of sed is:

CODE

sed -e 's/FROM/TO/g' old_file > new_file

that form substitutes all FROM concurrencies to TO in old_file and saves modified file under name new_file... for example:

CODE

sed -e 's/welcome/bye/g' welcome.sh > bye.sh

There is also similar utility, but it can translate single letters, it is tr. Mainly tr is used to change one letter to another:

CODE

tr 'letter1' 'letter2' < old_file > new_file

changes all letter1 ocurences to letter2 occurences in old_file and saves it under new_file. It can be used also with ranges, like in this example that converts lowercases to uppercases in file lower and saves it under file named upper:

CODE

tr [a-b] [A-B] < lower > upper

Another useful utility for formatting and parsing is tail – it make file read from specified line to end of file, it can be invoked by:

CODE

tail +line_number file

tail +1 file doesn't make any difference than cat file, but tail +2 file don't show first line of file. Another thing that can be used to format output are pipes – thing that allow you to format it at time... There are many pipes but I would cover three of them. Pipes are used by using structure command | command, you can use multiple pipes – command | command | command... . First of commands mainly used in pipes is awk...

awk is used to process column data, so you can display only one column of result, you can do this like that:

CODE

ls -l | awk '{print $5}'

not there are single quotes, not backquotes... mainly this looks like:

CODE

command(s) | awk '{print $collumn_number}'

of course awk have much more functionality, check man awk to see how powerful it is... Next utility is much smaller, but also useful for processing – it is head – it shows only n lines from file, for example:

CODE

ls -l | head -3

shows only first three lines of directory listing, mainly use of it looks like:

CODE

command(s) | head -n

so you can show only n lines from command's result. Next and last pipe I would talk about is grep – it is used to show only lines containing specified text, while using from shell it is very usefull while checking in checking pid of application to kill, then we use:

CODE

ps x | grep application_name

and we get pid, then we are killing troublesome application. Mainly this pipe looks like:

CODE

command(s) | grep text

and shows only lines from result that contains text. Let's look at some example... first of all we would list files in current directory that are owned by root, then we would take their size, sort it and give in result size of largest and in separate block form second to fifth size. We write results to file, count characters and append it at end of file...

CODE

#!/bin/bash

echo larger file in $PWD directory have `ls -l | grep root | awk '{print $5}' | sort -n -r | head -1` bytes > sizeanalizer.results
echo others have: >> sizeanalizer.results
ls -l | grep root | awk '{print $5}' | sort -n -r | tail +2 | head -5 >> sizeanalizer.results
echo and maybe less... >> sizeanalizer.results
SIZE=`wc sizeanalizer.results | awk '{print $3}'`
echo
>> sizeanalizer.results
echo >> sizeanalizer.results
echo to display text above this line, $SIZE characters were used >> sizeanalizer.results

it could be made easier, but the point is to demonstrate how these tools can be used...

And what if... - Test constructs...

As you see you can create now even complicated things... but what if you want to create file that exist or allow script to work only under exact circumstances? There you need to use if-else and case to do some test and steer script execution... right now our scripts were plain, now they got some depth and not always can execute same... it depends... but lets talk about test first...

test string must be contained between /b] ... [b or after test command – can be said this returns true or false, but don't shows them as strings. So lets see how test can be made (note spaces in [] - they are important !!!):

STRING TESTS:
[ stringA == stringB ] is true if strings A and B are equal
[ stringA != stringB ] is true if strings A and B aren't equal
[ -z string ] is true if string have zero length
[ string ] or [ -n string ] is true if string has non-zero length
[ stringA < stringB ] is true if string A is before string B (lexicographicaly)
[ stringA > stringB ] is true if string A is after string B (lexicographicaly)

NUMERIC TESTS:
[ num1 -eq num2 ] stands for equal
[ num1 -ne num2 ] stands for non-equal
[ num1 -lt num2 ] stands for less than
[ num1 -le num2 ] stands for less or equal
[ num1 -gt num2 ] stands for greather than
[ num1 -ge num2 ] stands for greater or equal

FILE TESTS:
[ -e file ] true if “file” exists
[ -d file ] true if “file” exists and is directory
[ -f file ] true if “file” exists and is regular file
[ -L file ] true if “file” exists and is symbolic link
[ -r file ] true if “file” exists and is readable
[ -w file ] true if “file” exists and is writable
[ -x file ] true if “file” exists and is executable
[ -s file ] true if “file” exists and it's length is non-zero value
[ -N file ] true if “file” have been modified since it was last read
[ file1 -nt file2 ] true if “file1” is newer than “file2”
[ file1 -ot file2 ] true if “file1” is older than “file2”

OTHER TESTS:
[ test1 -o test2 ] is true if true is any of tests
[ test1 -a test2 ] is true if true are both of tests...
true always true
false always false

warning, while using neasted tests don't use [ [ ... ] -o [ ... ] ] but only [ ... -o ... ], same for “-a”!!!

As you see there are many test and that aren't everything – they are only commonly used ones... So when you know how to make some test, let's go to if-else statement.

Mainly if-else structure looks like this (consider tabs and spaces to make it easier to read):

CODE

if expression
then
commands
else
commands
fi

when i write commands I mean any command and by expression some test or file execution... just let me show you some of possibilities:

CODE

if [ $a == $b ]
then
echo a is equal to b
else
echo a isn't equal to b
fi

this is simplest way... it also can be written like that:

CODE

if test $a == $b
then
echo a is equal to b
else
echo a isn't equal to b
fi

if you want to see if some application returned value 0 (exited normally) use:

CODE

if command
then
echo command exited normally
else
echo command exited abnormal, possible errors...
fi

of course commands can be another if...:

CODE

if [ $a == $b ]
then
echo a is equal to b
else
if [ $a == $c ]
then
 echo a is equal to c
else
 echo a isn't equal to b or c
fi
fi

this can be written short:

CODE

if [ $a == $b ]
then
echo a is equal to b
elif [ $a == $c ]
then
echo a is equal to c
else
echo a isn't equal to b or c
fi


As you see this can grow larger and larger every if-else... so what can we do about it? We can use case... It looks like this:

CODE

case variable in
value1 )
 commands1
;;
value2 )
 commands2
;;

...

* )
 default_commands
;;
esac

so – when variable is equal to value1, commands1 will be executed, if variable is equal value2, commands2 will be executed... if there isn't suited value, default_cammands will be executed... There is some example:

CODE

case $number in
1 )
 echo number is one
;;
2 )
 echo number is two
;;
3 )
 echo number is three
;;
* )
 echo I can count only to three
;;
esac

case values looks a little different than test... there is what can be used:

strict value, like 1, 2, “something”
logicaly added values, like value1 | value2 that stands for value1 or value2
range value, like [1-4] that stands for 1, 2, 3, 4 or [a-c] that stands for a, b, c

just case allows us to create useful code fragments, like this:

CODE

case $( arch ) in
i386 )
 echo 80386-based machine
;;
i486 )
 echo 80486-based machine
;;
i586 )
 echo Pentium-based machine
;;
i686 )
 echo Pentium2+-based machine
;;
*    )
 echo Other type of machine
;;
esac

how this would look using if-else? I can make you sure it would look longer... But what if you want to execute some fragment of code many times? You must use loops – loops that are allowed in bash are for and while, let me explain them... for should go first, it stands for “foreach” and looks like:

CODE

for variable in list
do
commands
done

how it works? It works like:

variable = first in list
commands
variable = second in list
commands
...


so – what is list? This is easy... lists are any variables or wildcards – exactly, variables that looks like:

CODE

variable=”value”

have one list element, but variable:

CODE

variable=” value  value value ...”

have many elements – and this is variable list... you can also create file wildcard list... that looks like:

CODE

*.sh

that contains all file names ending with .sh in current directory, or

CODE

/usr/locale/games/mygame/*my*.dat?

that contains all filenames that contains my and ends with .dat1, .dat2, .data or .dat^, but not .dat or .dat10 in directory /usr/locale/games/mygame

Let me show you some example of for usage:

CODE

LIST=".c .cpp .h"
for EXTENSION in $LIST
do
       for FILE in *$EXTENSION
       do
               echo $FILE
       done
done

that would list all files ending for .c, .cpp and .h in current directory, so you see for is very powerful, but it can't be executed exact times if we don't know the list or if we don't need to use any list... and there with help comes while loop:

CODE

while test_expression
do
commands
done

test expressions can be similar to if-else, but mainly there are used string and number tests, files tests are rather useless in this command... There is part simple example that counts from 1 to 100:

CODE

COUNTER=1
while [ $COUNTER -le 100 ]
do
echo $COUNTER
COUNTER=`expr $COUNTER + 1`
done

Every loop (for and while) can be stopped at any moment if it executes break command it goes behind loop. There is example of using break:

CODE

while true
do
echo you think this would be looping infinite?
break
done
echo WRONG ':)'

Now I would give you some example of use what we learned till now... I won't talk what it does, you should now what, if not, get back to earlier parts...

CODE

#!/bin/bash

EXTENSIONS=".tex .abc"
DIRECTORIES="design flash"

if [ -e /tmp/countertemp ]; then
echo "delete /tmp/countertemp to continue"
exit 1
fi

mkdir /tmp/countertemp

COUNTERALL="0"
COUNTER="1"

LINESALL="0"

for DIRECTORY in $DIRECTORIES
do
for EXTENSION in $EXTENSIONS
do
 COUNTERALL=`expr $COUNTERALL + 1`
 find $DIRECTORY -name *$EXTENSION -fprint /tmp/countertemp/_"$COUNTERALL"_.files
done
done

while [ $COUNTER -le $COUNTERALL ]
do
FILES="/tmp/countertemp/_"$COUNTER"_.files"
LINES=$(wc "$FILES"|awk '{print $1}')
LINE_NUMBER="1"
while [ $LINE_NUMBER -le $LINES ]
do
 FILENOW=$(tail +$LINE_NUMBER "$FILES"|head -1)
 LINENOW=$(wc "$FILENOW"|awk '{print $1}')
 LINESALL=`expr $LINESALL + $LINENOW`
 LINE_NUMBER=`expr $LINE_NUMBER + 1`
done
COUNTER=`expr $COUNTER + 1`
done
echo $LINESALL
rm -r /tmp/countertemp


A little more advanced thing for the end – read, bc, arguments, exit, functions...

This is almost everything – there are few small things left first of all – a float operations... this can be silly when you try to count percentage of something and get result 99/100=0 since 0.99 is taken down to 0 – so as you see float operations are required if you want to create some usefully counters... there is info how you can use them. I would only tell how to divide two variables, since other operations are similar...:

CODE

echo "scale=5; $A / $B" | bc

this would divide A by B and would show 5 places... This looks so strange, because bash can store only integer values, so floats are stored as string and we have to do some conversions... but it works...

Other thing I would mention at end are command line arguments so you can call you script not by:

CODE

command

but

CODE

command option1 oprion2 option3 ...

command line arguments can be accessed very easy – just use:

$0 gives you command name that was executed to run script (without arguments)
$1 stands for first argument
$2 stands for second argument
$# gives you number of arguments
$* gives you all arguments as list

they are considered usual variables, so you can for example do:

CODE

echo there are $# command line arguments...

So you know how to use command line arguments... was easy, right?

Probably you also want to communicate with user in other way – let user type something on keyboard and store it under variable... this is as easy as say – just type in script:

CODE

read VARIABLE
echo $VARIABLE

and you would see how it works... Ok... so we have that... and other thing... if your script executed other script you probably want to know if it’s succeed... there with help comes exit status... Just add to end of file exit with status number (0 means everything went ok, number form 1 to 255 means error) and you would know what happened to script... for example in scriptA executed from scriptB there is:

CODE

if [ -e config ]
then
exit 1
fi
...
exit 0

and in scriptB we have:

CODE

if scriptA
then
echo config created
else
echo error ocured – config file present...
fi

And last thing that left to make you skilled shell programmer – functions... why use them? To make your script clean and easy to read, to not write same thing few times, there are many “because” so there is how you can define function:

CODE

somefunction () {
echo Argument number 1 $1
echo Argument number 2 $2
}

then you can call it by typing:

CODE

somefunction arg1 arg2

Oh man – really long tutorial, don't you think? But say... you can now write almost all scripts that you may want! Wasn't it worth to spend some time and read it? :P

Any way – that is it – if you have some question just ask in this topic and I for sure would answer it... If you would find error (I'm just human...) also post it there I would check it again and PM moderator or admin to change it...

Hope you liked it

Regards

Giniu




PS.: If there is such need I can write another tutorial – essential Linux commands in which I would cover all topics that I said you have to start with it like using of commands like cp, mv, rm and basic Linux usage like changing mode to console and so on... everything that would fill the hole in wisdom... I would be happy to take a part in filling it :P Also if I made some mistake - just post fixed example/information so moderators/admins would be able to join it into this tutorial with note who fixed it :P

PPS.: Thanks to Nelle for checking language things - english isn't my primary language... keep this in mind...

   Tue Apr 26, 2005    Reply         

WOW
man thats a hard work
tnx
this gona help me alot :)

NoMore

   Sat Nov 11, 2006    Reply         

thank youShould I “bash” Or “bash”?

I went thru your tutorial and wanted to say kudos for putting this all together.

great job!

-feedback by coolmn

 

   Sat Nov 13, 2010    Reply         


Quickly Post to Should I “bash” Or “bash”? Or writing scripts in Bourne Again Shell w/o signup Share Info about Should I “bash” Or “bash”? Or writing scripts in Bourne Again Shell using Facebook, Twitter etc. email your friend about Should I “bash” Or “bash”? Or writing scripts in Bourne Again Shell Print
Reply / Comment New Discussion / Topic Share / Bookmark E-Mail a Friend Print

Similar Topics:

PHP Good Comments Make Good Script...

So this is a basic programing tool that is too often overlooked. I learn programing by looking at other peoples work and figuring out what each command does. Having comments in the code provides me with an idea of what the author was thinking while writing that part of the code. ...more

   20-Feb-2005    Reply         

Any MS Word Plugin For Writing Movi...

hi wats up..well i just have a question for you guys..becase some of you can probably answer it well first off my name is jude and i like to write movie scripts but i dont have a software ...more

   16-Jun-2005    Reply         

Bash, Where-to And How-to

Bash is a linux scripting language. Bash or bourne again shell is the basic thing when any Linux is concerned. As far as scripting in this laguage is concerned the best and the most precious tuorial of it is placed here: http://www. ...more

   10-Aug-2005    Reply         

Installing Photoshop 7 On Linux (fedora) Needs windows installation   Installing Photoshop 7 On Linux (fedora) Needs windows installation (10) (0) Adding Ttf Fonts To Your Linux so you can really get into gimping :)  Adding Ttf Fonts To Your Linux so you can really get into gimping :)