Bash Basics
- Use
echo $PATH
to display the available executable paths for your environment before you start writing your script.- NOTE: Your default
$PATH
can be modified and appended:- Use:
PATH=$PATH:/something/something/bin
- Use:
- Add
/home/user/bin
and place scripts there to enable easy system-wide script execution.
- NOTE: Your default
- To run a script not located in
/bin
reference the full script path, as in "/home/user/myscript
" or use./myscript
to run it. - REMEMBER: Scripts must have proper executable permissions to be able to run.
- Perform
chmod u+x <scriptname>
to enable script execution.
- Perform
The Simplest Possible Bash Script
A script can be comprised of a single Linux command, as in this example I'm calling "myls
":
#!/bin/bash
ls
Once you have written this and run myls
the script simply runs ls
on the current working directory.
We can replace ls
with any other command we wish. Try out some of these commands in a bash script to see what they do:
echo $USER
orwhoami
pwd
env
echo $HOME
echo $HISTFILE
echo $HOSTNAME
echo $PS1
echo $SHELL
My dirlist
Script
This made-up script, "dirlist" (see: my github repository), performs an ls
on a given directory and logs the results to a log file in the user's $HOME directory. The syntax to run it is simply: dirlist </directory>
.
dirlist
terminal output:
For example, the output to the terminal when I ran "dirlist /home
" on one of my servers was as such follows:
Operation Complete: Logged to /root/dir_list_log.
#### /home <ON> 22/03/2016 <AT> 01:53:31 AM ####
file1.txt
file2.txt
file3.txt
file4.txt
user
>>>> End of listing for /home. <<<<
Notice I added some formatting: the #### /home ...
line with the time and date when the command was run, plus a 'end of listing' line. The 'operation complete' message shows where the results were logged.
dirlist
log format:
When I viewed the contents of the log that was created when I ran the program (using cat $HOME/dir_list_log
), I found that the following entry had been appended — as expected:
#### /home <ON> 22/03/2016 <AT> 01:53:31 AM ####
file1.txt
file2.txt
file3.txt
file4.txt
user
>>>> End of listing for /home. <<<<
This "dirlist" script does one thing and one thing only: print out and log the contents of a directory (see the script in its entirety below). It has limited utility, for sure... If we were being creative, perhaps we could imagine using it to keep track of a given directory's contents over time, setting it to be run automatically as a cronjob every x-number of minutes on the /etc
directory or some other important system location. The fact that it appends to the log while running ls
is key here (>>
redirect).
The value of this script is mainly pedagogical; that is, it is a teaching and learning tool. It implements the date
command, inserting current time/date into the log and console output. It shows off the >>
redirect to append output to a file, which as stated above, can be quite useful for creating and maintaining logs of various system events and user actions.
Here is the "dirlist" bash script, followed by some final comments on the use of arguments and the if/then condition statements being used:
< view on github >
#!/bin/bash
# log/append contents of given directory to $HOME/dir_list_log with timestamp.
# create $location variable for re-use within the program
location=$1
# exit, print 'error' message if no $location argument given
if [ -z "$location" ]
then
echo "ERROR: Please provide a directory location to index."
exit
fi
# create/append header message to dir_list_log file in $HOME
# include location given as argument
# include time and date of directory content retrieval
echo "#### $location <ON> \
`date +\"%d\"\/\"%m\"\/\"%Y\"` <AT> \
`date +\"%r\"` ####" \
>> $HOME/dir_list_log
# list the contents of directory given as input to command 'dirlist'
# output/append listing to dir_list_log
ls $location >> $HOME/dir_list_log
# create/append 'end of listing' message to dir_list_log
echo ">>>> End of listing for $location. <<<<" >> $HOME/dir_list_log
echo " " >> $HOME/dir_list_log
# output 'operation complete' message to console
echo "Operation Complete: Logged to $HOME/dir_list_log."
echo " "
echo "#### $location <ON> \
`date +\"%d\"\/\"%m\"\/\"%Y\"` <AT> \
`date +\"%r\"` ####"
ls -1 $location
echo ">>>> End of listing for $location. <<<<"
echo " "
Final Thoughts on the "dirlist" Script:
Two more important features to highlight:
location=$1
andls $location
-- The argument variable definition comes at the beginning of the script. The argument can then be fed todirlist
by the user by naming the directory to index.if [ -z $location ] ...
-- This if/then condition statement ensures that users enter a directory location as an argument. The script exits with an error message in this case. More on[
and thetest
commands later.
Variables & Arguments
(( ... ))
-- used to interpret arithmetic operations$#
-- display the number of arguments passed to the script. one use-case is:
if (( $# == 0 ))
then
echo “No arguments have been passed to the script”
fi
{<VARNAME>}
-- Place curly braces around variable names to unambiguously identify them
VARIABLE=hello
echo $VARIABLE
= hello
echo $VARIABLE1234
-- nothing happens since$VARIABLE1234
is not a variableecho ${VARIABLE}1234
interprets the variable as is and adds on '1234' at the end:echo ${VARIABLE}1234
= hello1234
Conditions in bash Scripting
[
--test
a given statement. output: (0) for True or (1) for False.[[
-- anothertest
command which allows for arithmetic operations and combining of multiple tests without using escape characters, such as "/
":- The following test statement returns
TRUE
:[[ ( 3 -gt 2 ) && ( 4 -lt 5 ) ]] && echo TRUE || echo FALSE
- Read as: If 3>2 AND 4<5 then "TRUE"; else "FALSE".
- The following test statement returns
3 filetest
Example Programs:
Using the
test
command:#!/bin/bash
# "filetest" script returns 'true' message if it is a file
FILE=$1
# syntax: filetest <file-or-dir-name>
if test -f $FILE
then
echo "$FILE is a file"
fi
Shorthand version: Uses the "
[
test" plus&&
to execute theecho
confirmation message only if[
test returnstrue
:#!/bin/bash
# "filetest" script returns 'true' message if it is a file
FILE=$1
# syntax: filetest <file-or-dir-name>
[ -f $FILE ] && echo "$FILE is a file"
Using
if
and[
test together:#!/bin/bash
# "filetest" script returns 'true' message if it is a file
FILE=$1
# syntax: filetest <file-or-dir-name>
if [ -f $FILE ]
then
echo "$FILE is a file"
fi
The single bracket "[
" if statements here are referred to as "test" brackets and are the oldest and most cross-compatible form of "test" command.
Basic syntax rules:
* You must quote every string
* No file globbing is allowed with single bracket test
Sample Script "mt
" to Check for Empty String Variable:
#!/bin/bash
# "mt" = empty (string) checking tool
MTSTRING=""
if [ -z "$MTSTRING" ]
then
echo "Variable is empty."
else
echo "Variable is NOT empty; set to $MTSTRING."
fi
If you create this script and then run it using mt
from the command line, you will see it return the 'variable is empty' message.
If you edit the MTSTRING
variable in the script file itself, e.g. "MTSTRING=Hello World
", and then run mt
again, you will get the non-empty confirmation message: "Variable is NOT empty; set to Hello World."
NOTE: Withing single bracket "[
" test statements, you must always put double-quotes around string variables. With double bracket tests "[[
" this is not always required, but is considered best practice.
Flag Conditions:
-gt
-- greater than-lt
-- less than-ge
-- greater than equal to-le
-- less than or equal to-eq
-- equal to-ne
-- not equalto-f
-- is file-d
-- is directory-l
-- is symlink
Sample 'Greater-than' Test Script:
if [ 5 -gt 4 ]
then
echo "TRUE: 5 is greater than 4."
fi
Double test-brackets: [[ ... ]]
[[
-- does allows shell globbing, unlike[
*.txt
-- expands to[anything].txt
- other features/limitations:
- word-splitting is prevented (double-quotes not needed)
- omitting double-quotes around string variables is not considered best practice
Two Sample check_string_contents
Programs:
MYSTRING=sammie
if [[ "$MYSTRING" == *mmie* ]]
then
echo "TRUE"
fi
This first example determines if the given string "sammie" contains "mmie". This will return TRUE
.
MYSTRING=sammie
if [[ $MYSTRING == *[sS]a* ]]
then
echo "TRUE"
fi
This second example determines if the string contains either "sa" or "Sa". Returns TRUE
. Notice this second example contains no double-quotes.
Expanding files names using [ ... ]
and [[ ... ]]
Sample program to return true
if only one "*.txt" file exists in directory:
if [ -a *.txt ]
then
echo "Single test brackets with * expands to entire current directory."
echo "Produces error if more than 1 file exists."
echo "ERROR: There is at least one file that ends with \".txt\" in this directory."
fi
Sample program with double-brackets test returns true
only if literal "*.txt
" file exists:
if [[ -a *.txt ]]
then
echo "TRUE: There is a file named \"*.txt\" exactly."
fi
Double brackets also allow for:
&&
-- "and" operator||
-- "or" operator=~
-- regular expression comparison operator:=~
takes a string on the left and an extended regular expression on the right.=~
returns a "0" for 'success' if the regular expression matches the string=~
returns a "1" for failureThe example script below from user paxdiablo on Stack Overflow tests whether the value assigned to variable "
$i
" is a valid year, either 2007 or 2008:
i="test" if [[ $i =~ 200[78] ]] ; then echo "OK" else echo "not OK" fi
With "test" it returns
not OK
; with "2007" it returnsOK
.
And/Or Logic Operators and Alternatives:
&&
-- same asand
for[[
&&
-- also same as[ EXPR1 -a EXPR2 ]
||
-- same asor
with[[
||
-- also same as[ EXPR1 -o EXPR2 ]
Functions of Double Parentheses:
(( ... ))
-- used primarily for number-based conditions- same as using
let
command - allows for the use of
>=
operators - allows for the use of
&&
and||
- flag conditions not allowed
-a
and-o
also not allowed
- same as using
Further Reading: