====== 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 [[http://google.com|Google]] and find a suitable tutorial according to your preferences. See also [[courses:ae4b33oss:troubleshooting#qi_can_not_execute_my_scripts|Q/A]]. ===== Your first script ===== BASH script is a simple text file: - The first line is called [[http://en.wikipedia.org/wiki/Shebang_(Unix)|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) - Use ''#'' for comments - Continue with your commands, i.e.: * ''echo "Hello world!"'' - Save your file (you can use a ''.sh'' suffix), and (IMPORTANT) - Add an ''exec'' permission for the owner: * chmod u+x ./file.sh - 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 [[http://en.wikipedia.org/wiki/KISS_principle|KISS principle]]. Read more about [[http://en.wikipedia.org/wiki/Unix_philosophy|Unix philosophy]]. ===== An example ===== - Save the following code - Set the ''exec'' permission (see above) - Execute it - Correct the syntax error - 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 commands((There are other solutions, but the one with sub-process redirection is not posix compliant...)): 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//'' * ''/dev/udp//'' 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>&-