Warning
This page is located in archive.

About BASH Scripts

This page shows a basic information on how to create and execute a simple script in BASH. I suppose that you can also use Google and find a suitable tutorial according to your preferences. See also Q/A.

Your first script

BASH script is a simple text file:

  1. The first line is called Shebang and tells the system (exec function) which interpreter you want to use. In case of BASH it should be one of:
    • #!/bin/bash
    • #!/usr/bin/env bash (more portable)
  2. Use # for comments
  3. Continue with your commands, i.e.:
    • echo “Hello world!”
  4. Save your file (you can use a .sh suffix), and (IMPORTANT)
  5. Add an exec permission for the owner:
    • chmod u+x ./file.sh
  6. Execute the file:
    • ./file.sh

General notes

  • Please resist the obsession to make your script interactive. An usual Unix script should be driven by commandline parameters and should process stdin whenever appropriate. See for example the simple cat program.
  • Comment your code
  • Follow at least the KISS principle. Read more about Unix philosophy.

An example

  1. Save the following code
  2. Set the exec permission (see above)
  3. Execute it
  4. Correct the syntax error
  5. Follow the TODO instructions

#!/usr/bin/env bash
 
echo "Hello world!"
 
#Set an int and string variables
#Note that there must not be any spaces around '='
A=3
B="1 2 3"
 
echo "The value of A is: ${A}"
echo 'No substitution here: ${A}'
 
  # *** TODO(students): Modify the script to print the C and D variables
C="1 2 3 ${B}"
D='1 2 3 ${B}'
 
#a simple condition: see 'man test'; IMPORTANT: watch carefully where the spaces are
  # ***TODO(students): go on and try to test a string (use ''='', ''-n'', etc.)
if [ "$A" -eq 3 ] ;then
  echo "A is equal to 3"
else
  echo "A is not equal to 3"
fi
 
#remember that '[' is an alias for the 'test' command. Actually, any command can be used
if date ; then
  echo "You should see that the 'date' command has been executed within the 'if' condition"
fi
 
#and a very simple loop (over a list); IMPORTANT: watch carefully where the spaces are
  # *** TODO(students): Modify the loop using 'case' command and test a few possibilities, i.e.: 2, a, ...
for i in 1 2 3 a b c; do
  echo "i=${i}"
done

How to get value from a subshell

You might have noticed, that if there is a while loop after a pipe, it is not possible to return a value from the inside, i.e. a counter.

x=0
ls /dev | while read file ; do
  x=$((x+1))
done
 
echo $x       #prints '0' (!)

And this is not what we wanted. A solution is to use subshell and a list of commands1):

x=0  #needed to reset the var, it might have been reused when in a sub-loop
 
x=$( 
  ls /dev | { 
    while read i ; do 
      x=$((x+1)) 
    done
    echo $x 
  } 
) 
 
echo "x=$x"    #prints correct number of items

How to correctly process cmdline parameters

This is one of the possible solutions. You might also consider the getopts bash built-in (see man bash for more information).

#!/bin/bash
# Description of the purpose of the script (TODO)
# Usage info: $0 [OPTIONS] path (TODO)
#                    OPTIONS:
#                                       -v/--verbose    Be verbose
#                                       -c/--count  NUM Numeric parameter example  (limit) Default: 1
 
#Exit codes
ERR_PARM=1		#Incorrect parameters
ERR_PATH=2		#Incorrect path
 
#Script control variables (remember to define the defaults)
#1/0 specifies the verbose output
VERBOSE=0
CNT=1
TARGET_PATH='.'
 
if [ $# -eq 0 ] ; then
	echo "Specify at least one parameter" >&2
	echo "Usage: $0 [OPTIONS] path" >&2
	exit "${EXIT_PARM}"
fi
 
#Now we have at least one parameter, we will loop throug them and (and shift them)
 
while [ -n "$1" ]  ; do	#if the first parameter is available, process it
	case "$1 " in 
 		"-h|--help" ) 
			#print usage info and exit with exit code 0
			#what about writing an usage() function? (TODO)
			;;
		"-v|--verbose" )
			VERBOSE=1
			;;
		"-c|--count" )
			#here we have to test if "$2" is available and act accordingly (TODO)
			N="$2"
			shift		#and we have to shift the parameters left
			;;
		*)	
			#This should be a path. However you need to test it. (TODO)
			TARGET_PATH="$1"
	esac
 
	#Shift all parameters to the left (discard the first one)
	shift
done
 
#And frome here below we should only use the defined variables as all the commandline params have been processed:
#	CNT
#	TARGET_PATH
#	VERBOSE
 
if [ ${VERBOSE} -eq 1 ] ; then 
	echo "Verbose debug output to stderr..." >&2
fi
 
...

How to use TCP/UDP from BASH

There are two sockets available:

  • /dev/tcp/<host>/<port>
  • /dev/udp/<host>/<port>

You can use these, provided that port and host is a valid port number and hostname/IP respectively. An example follows:

HOST="www.ciirc.cvut.cz"
PORT=80
 
#Open the socket using FD 3 filedescriptor
exec 3<>"/dev/tcp/${HOST}/${PORT}"
 
#Write HTTP request to FD 3
printf "GET / HTTP/1.0\r\n" >&3
printf "Accept: text/html, text/plain\r\n" >&3
printf "Accept-language: en\r\n" >&3
printf "\r\n" >&3
 
#Read response and print it
#Cannot use the 'read' in the while condition, as it returns
# exit code of 1 when EOF is reached, however the 
# line variable still might contain data
# You can also simply use cat <&3 to just print the data to stdout
while : ; do
	read -u 3 line
	exit_code=$?
	echo $line
	[ $exit_code -eq 0 ] || exit 0
done
 
#Close FD 3
exec 3>&-

2014/09/30 11:28 · bursam
1)
There are other solutions, but the one with sub-process redirection is not posix compliant…
courses/ae3b33osd/bash_script.txt · Last modified: 2015/02/16 16:06 by bursam