Table of Contents

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

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:

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>&-

1)
There are other solutions, but the one with sub-process redirection is not posix compliant…