The shell script file
By default we name a script file with the .sh extension and the first line is usually (although not strictly required) to be one of the following
#!/bin/sh OR #!/bin/bash
The use of sh tells Linux we want to run the script with the default shell script . Note that this might be dash, bash, bourne or any other shell available, hence when using sh the developer of the script needs to be aware that they cannot expect bash (for example) capabilities to exist and therefore if bash specific code exists within the script the #!/bin/bash line should be used.
Note: #! is known as the sha-bang
What shell am I running?
You can use $SHELL in your scripts or from the command line, for example
echo $SHELL
Making the script executable
chmod 777 myscript.sh
Comments
Comments are denoted by #. Anything after the # until a new line, will be seen as a comment, i.e.
echo "Some text" # write to stdout "Some text"
Variables
We can create variables, which are case-sensitive, like this
VARIABLE1="Hello" variable2="World" echo $VARIABLE1 $variable2
Note: You should not have spaces between the = operator or the command may not found. So for example this will fail VARIABLE1 = “Hello”
Whilst we can use variables that are not strings, underneath they’re stored as strings and only converted to numerical values when used with numerical functions etc.
So for example
i=0 $i=$i+1
will fail with 0=0+1: command not found. To increment a variable (for example) we need to use the following syntax
let "i=i+1" #OR i=$((i+1))
we can also use postfix operators, i.e. ++ or += such as
let "i++" #OR ((i++)) #OR let "i+=1" #OR ((i+=1))
We can also create arrays using the following syntax
a=("a" "b" "c")
and an example of indexing into this array is as follows
echo "Array element at index 1 is ${array[1]}" # outputs Array element at index 1 is b
We can also remove or unset a variables like this
unset i
Logic operations
IF, THEN, ELSE, ELIF…
As our scripts become more capable/complex it’s likely we’ll want to use some logic and branching code, i.e. IF, THEN, ELSE, ELIF code. Let’s look at an example of IF, THEN syntax
if [ $i = 6 ] then echo "i is correctly set to 6" fi
Note: after the space after the [ and before ] without this the script will error with command not found.
The [ ] syntax is the way you’ll often see this type of operation, from my understanding this is actual an alternate syntax to test i=6, so for example
test i=6; echo "i is correctly set to 6"
Note: the example above shows the test on a single line, in this case the ; is used to denote the end of the line.
We can use = or -eq along with less than, greater than etc. however these standard operators are to be set to use a string comparisons, i.e. we do not use <, instead we use -lt for non-strings, like wise = will do a string comparisons whereas -eq will handle numerical comparisons.
We can also use ELSE for example
if [ ! -d "BAK" ] then echo "BAK does not exist" else echo "BAK exists" fi
and finally ELIF
if [ -d "BAK" ] then echo "BAK exists" elif [ -d "BACK" ] echo "BACK exists" fi
Checkout If Statements! which has a list of the operators in more depth.
[[ ]] vs [ ]
The [ ] is actually just an alias for test as mentioned above. BASH and some other shells also support [[ ]] syntax which is more powerful. See What is the difference between test, [ and [[ ? for more information.
Case (switch)
Extending the IF, THEN, ELSE, ELIF we also have a switch style comparison capability, for example
case $response in y|Y) echo "Executing script" ;; *) exit ;; esac
The syntax for y|Y) is pattern matching and ) terminates the pattern. This is followed by one or more statements to be executed followed by the ;; terminator. The *) means match against anything else (or default condition). We then terminate the case block with esac. So in this example we’ll output “Executing script” if the response variable is either y or Y.
Loops
We can run commands in loops such as for, while and until.
While and Until are very similar except that while keeps looping whilst a condition is true whereas until loops, until a condition is true. Here’s a couple of simple examples
i=0 while [ $i -lt 10 ] do echo $i ((i++)) done until [ $i -lt 0 ] do echo $i ((i--)) done
for loops using a similar syntax again, except they use the in keyword, for example
array=("a" "b" "c") for item in ${array[@]} do echo $item done
This example demonstrates looping through an array, but we can also loop through items returned by shell commands, for example
for item in $(ls -a) do echo $item done
In this example we’re looping through the results of the command ls -a. Although a better solution to this might be
for item in ${PWD}/* do echo $item done
The ls version returns multiple items for a file name with spaces, so not too useful if we want each file name including spaces.
Here’s a final example using using the back tick (`) which can be used to enclose commands, for example in this instance we execute the command seq 1 10
for item in `seq 1 10`; do echo $item done
Passing arguments to your shell script
Arguments are passed into your script via the command line as you’d normal do, i.e. in this example my shell script (myscript.sh) takes two arguments Hello and World
./myscript.sh Hello World
To reference the arguments in the script we simply use $1 and $2. i.e.
echo $1 # Should be Hello echo $2 # Should be World
There also there’s also the $@ which denotes all arguments, i.e.
echo "$@"
Will output all the arguments passed into the script or function.
Functions
We can create functions inside our shell scripts and/or include other script files which have functions etc. within.
You need to declare the function before usage and writing a function is pretty simple, i.e.
say_hello() { echo "say_hello called" } say_hello
To include arguments/parameters we use the same system as passing arguments via the command line, so for example
say_something() { echo "say_something says $1 $2" } say_something Hello World # outputs say_something says Hello World
here we see the arguments are turned into the $1 and $2 variables, but of course local to our function.
STDIN/STDOUT/STDERR
We’ve already seen that echo is the equivalent of output to STDOUT in it’s default usage, although in can be used to output to STDERR, see Illustrated Redirection Tutorial.
We can use
In it’s most basic use we can write the following
read input
Where input is a variable name.
We can also use it in slightly more powerful ways, such as
read -n1 -p "Are you sure you wish to continue (y/n)?" input
In this case we read a single character (-n1) with the prompt (-p) “Are you sure you wish to continue (y/n)?” into the variable named input.
The read function can also be used to read data from a file by using a file descriptor and the argument -u.
References