Saturday, November 27, 2010

ShellScript Nightmares


Objective: SOS with "Hell" scripting ;)


This post is a scratch book to collect some tips about shell script programming that have caused me some silly headaches since I started using Linux and Its useful shell scripting features


Debugging


When you are in trouble maybe this can help and provide you better debugging info than when running your shell script normally
sh -x shellscript.sh


IFS


IFS is a system var that defines what is considered the field separator in a list when being parsed by a loop. If you don't define it properly you won't get the desired results.This
for i in `cat /etc/services`; do echo $i; done

is not the same as this
IFS=$'\n';for i in `cat /etc/services`; do echo $i; done

So for god shake be very careful with the ' marks and the $ ...why this?? i took me enough time to discover this to research why, anyway Ill be graceful if you sent me an email in case you know

String assignment


As a good programmer or good-wannabe programmer you may want your code to be clean an readable, thus you may assign values something like this foo = 7. NOPE NOPE NOPE, your shell is not going to like it, it expects foo=7, this is no spaces in between

Conditionals


If clauses can be used like this:
if [ expression ]
then
    <actions_if_true>
else
    <actions_if_false>
fi
#or
[[ <expression> ]] && <actions_if_true> || <actions_if_false>
#or using the "test" clause

It is very important to respect the blank spaces between the brackets
In order to use double conditions, it should be
# expression1 and expression2 must be true
if [ expression1 -a expression2 ]
if [ expression1 ] && [ expression2 ]
# either expression1 or expression2 have to be true
if [ expression1 -o expression2 ]
if [ expression1 ] || [ expression2 ]


String comparison


After reading the last tip you would use string comparison inside conditional clauses like this:
if [ $foo="something" ]
...

NOOOOOOOOOOOOPE!! Shell scripting does not like it this way, you should separate it or It will understand you are just assigning. This should be correct:
if [ $foo = "something" ]
...

By the way take care with strings that contain blank spaces, in that case your comparison should look like:
if [ "$foo" = "something containing blank spaces" ]
...


Command output assignment


In the same way you assign a value to a variable you can assign the output of a function or external command, to do so you would use the following syntax:
foo=`cat /etc/services`

Bear in mind there shouldn't be any blank space surrounding the = sign

Number comparison


When you are comparing numbers in a conditional clause you must do it in the following way, this is, using -eq, -lt, -gt, etc.
if [ 1 -eq $i ]

Bear in mind there shouldn't be any blank space surrounding the = sign

Function return value


Shell script does not have "return" clause, therefore the first value you "echoed" inside a function is taken as the return value. Wait I didn't explain how to define a function:
function_A(){
sum=`expr $1 + $2`
echo "Im the return value:$sum"
}

param1=1
param2=2
echo `function_A $param1 $param2`

Keep an eye on the following things
  • The function declaration does not declare parameter types and therefore parentheses are always with no parameters inside
  • The parameters are accesses as $N where "N" is the number of the parameter ($1=param1,$2=param2)
  • In the function invocation the parameters are specified next to the function name without parenthesis.

Debugging help


When you have nested loops you want to stop things to see how the script is running, you can achieve this by using a "readline" stop that will be continued after you press the enter key:
...
read $foo
...


Floating point calculation


Believe it or not but you cannot use decimal numbers inside shell script, therefore you carry those decimal/floating point numbers contained in string variables and then you operate them with a command called bc. An example can be seen below:
function_A(){
sum=`echo "$1+$2"|bc`
echo "Im the return value:$sum"
}

param1=1.2
param2=2.8
echo `function_A $param1 $param2`


Strictly integer variables


Variables in Shellscript tend to be types-bisexual they can accept strings or numbers. In some conditional clauses we see that the shellscript parser is complaining about the types that should be equal. For this cases you can define an strictly int variable and assign it a number from a string variable that will be casted to integer.
max_hits=10
typeset -i hit=0
hit="4"
if [ $max_hits -lt $hit ]
...

Otherwise in the if clause we would see an "unary operation expected" exception.

Bash arrays


wow yeah! Bash shell do handle arrays, its usage is describe in the following examples
#instantiation example
array=(11 2 3 4 5 10 8)
#or
array[0]=0
#length
${#ports[*]}
#read form it, $i is a number
${ports[$i]}
#write to it, $i is a number
ports[$i]=7


Concurrent execution


Commands in a shellscript are executed in sequential order, this is command in line 4 won't be executed before command in line 3 is finished. If we wanted the script not to wait for certain commands or group or commands we use the & symbol.
The sequential execution 4 will appear before the execution 3 because the concurrent lines do not make the script wait for them

No comments:

Post a Comment