Difference between revisions of "Bash Tips"

(Created page with "Use our feedback form to suggest a Linux related topic and we will publish it for you Click here to suggest Home About MyLinuxBook Authors needed Contact us...")
 
Line 109: Line 109:
 
ARG : If not defined “$@” is used otherwise we can specify a custom string to be parsed.
 
ARG : If not defined “$@” is used otherwise we can specify a custom string to be parsed.
  
+
 
  
 
Here is a list of variables used by Getopts for parsing
 
Here is a list of variables used by Getopts for parsing
Line 117: Line 117:
 
OPTERR : It can have two values If its value is 0 error messages will no be displayed. If its value is 1 error messages will be shown.
 
OPTERR : It can have two values If its value is 0 error messages will no be displayed. If its value is 1 error messages will be shown.
  
+
 
  
 
Error Reporting
 
Error Reporting
Line 126: Line 126:
 
Verbose Mode : If invalid option is seen, getopts places ‘?’ into NAME and unsets OPTARG. If a required argument is not found, a ‘?’ is placed in NAME, OPTARG is unset, and a diagnostic message is printed.
 
Verbose Mode : If invalid option is seen, getopts places ‘?’ into NAME and unsets OPTARG. If a required argument is not found, a ‘?’ is placed in NAME, OPTARG is unset, and a diagnostic message is printed.
  
#!/bin/bash
+
# !/bin/bash
# cmd.sh -v -r -f <text>
+
# cmd.sh -v -r -f
 
while getopts ":vf:r" opt;do
 
while getopts ":vf:r" opt;do
 +
 
     case $opt in
 
     case $opt in
 
         v)
 
         v)
Line 149: Line 150:
 
There is one limitation with getopts that it can not handle long option name like -abc or –option. To handle long option names there is another tool getopt (note missing ‘s’) which is an external tool.
 
There is one limitation with getopts that it can not handle long option name like -abc or –option. To handle long option names there is another tool getopt (note missing ‘s’) which is an external tool.
  
+
 
 
Arrays
 
Arrays
  
Line 185: Line 186:
 
We can also declare empty array using the keyword declare. declare -a months_array This creates an empty array with no values in it.
 
We can also declare empty array using the keyword declare. declare -a months_array This creates an empty array with no values in it.
  
+
 
 
Accessing Array Elements
 
Accessing Array Elements
  
Line 207: Line 208:
  
 
for i in ${month_names[@]};do
 
for i in ${month_names[@]};do
 +
 
     echo $i  
 
     echo $i  
 
done
 
done
Line 215: Line 217:
  
 
for ((i=0;i<${#month_names[@]};i++));do
 
for ((i=0;i<${#month_names[@]};i++));do
 +
 
     echo ${month_names[$i]}
 
     echo ${month_names[$i]}
 
done
 
done
Line 258: Line 261:
 
function push()
 
function push()
 
{
 
{
 +
 
     __STACK__[$__STACK_TOP__]="$*"
 
     __STACK__[$__STACK_TOP__]="$*"
 
     __STACK_TOP__=$((__STACK_TOP__+1))
 
     __STACK_TOP__=$((__STACK_TOP__+1))
Line 265: Line 269:
 
function pop()
 
function pop()
 
{
 
{
 +
 
     if [ $__STACK_TOP__ -gt 0  ];then
 
     if [ $__STACK_TOP__ -gt 0  ];then
 
         __STACK_TOP__=$((__STACK_TOP__-1))
 
         __STACK_TOP__=$((__STACK_TOP__-1))
Line 276: Line 281:
 
function peek()
 
function peek()
 
{
 
{
 +
 
     if [ $__STACK_TOP__ -gt 0  ];then
 
     if [ $__STACK_TOP__ -gt 0  ];then
 
         echo "${__STACK__[$((__STACK_TOP__-1))]}"
 
         echo "${__STACK__[$((__STACK_TOP__-1))]}"
Line 292: Line 298:
 
This statement inside a function will create a function local variable.
 
This statement inside a function will create a function local variable.
  
+
 
 
Exceptions or Traps
 
Exceptions or Traps
  
Line 301: Line 307:
 
command can be function some script or a simple command.
 
command can be function some script or a simple command.
  
#!/bin/bash
+
# !/bin/bash
#trap example
+
# trap example
  
 
function runme
 
function runme
 
{
 
{
 +
 
     echo "Signal received" exit 0
 
     echo "Signal received" exit 0
 
}
 
}
Line 312: Line 319:
  
 
while true; do
 
while true; do
 +
 
     sleep 1
 
     sleep 1
 
done
 
done
Line 329: Line 337:
  
 
$ exec 5 <>file.txt
 
$ exec 5 <>file.txt
 
 
This command will open file file.txt and assign the file descriptor 5.
 
This command will open file file.txt and assign the file descriptor 5.
  
Line 338: Line 345:
  
 
To handle redirection we can use following format m>&n Redirect output of file descriptor m (ms is default to 1) to file decrioptor n m<&n Redirect input of file descriptor m ( ms is defualt to 0 ) to file decrioptor n
 
To handle redirection we can use following format m>&n Redirect output of file descriptor m (ms is default to 1) to file decrioptor n m<&n Redirect input of file descriptor m ( ms is defualt to 0 ) to file decrioptor n
 
 
Redirecting Standard Output to file descriptor
 
Redirecting Standard Output to file descriptor
  
Line 398: Line 404:
  
 
$ read line < /tmp/test
 
$ read line < /tmp/test
 
 
This read will blocked until it read something from fifo file.
 
This read will blocked until it read something from fifo file.
  
Line 413: Line 418:
 
for example
 
for example
  
exec 7<> /tmp/test
+
exec 7<> /tmp/testwhile true;do
while  true;do
+
 
 
     read -t0 -u7 line
 
     read -t0 -u7 line
 
     if [ $? -eq 0 ];then
 
     if [ $? -eq 0 ];then
Line 432: Line 437:
  
 
$ mkfifo /tmp/myfifo
 
$ mkfifo /tmp/myfifo
$ cat /tmp/myfifo | /bin/sh | nc -l 1356 > /tmp/myfifo
+
$ cat /tmp/myfifo | /bin/sh | nc -l 1356 > /tmp/myfifo
  
 
This simple command will produce a netcat server running a shell which can be accesed using command
 
This simple command will produce a netcat server running a shell which can be accesed using command
Line 440: Line 445:
 
If you want to access the shell from a remote machine just change the localhost to the IP address of the machine and you get yourself a remote shell.
 
If you want to access the shell from a remote machine just change the localhost to the IP address of the machine and you get yourself a remote shell.
  
+
 
 
Indirect Reference
 
Indirect Reference
  
Line 454: Line 459:
 
Lets create a program which will print the frequency of each word.
 
Lets create a program which will print the frequency of each word.
  
#!/bin/bash
+
# !/bin/bash
#!/bin/bash
+
# !/bin/bash
 
# word_freq
 
# word_freq
 +
 
function usage
 
function usage
 
{
 
{
 +
 
     echo "usage: $0"
 
     echo "usage: $0"
 
}
 
}
  
 
# Check argument is given and it is a file
 
# Check argument is given and it is a file
 +
 
if [ -z "$1" -o ! -f "$1" ];then
 
if [ -z "$1" -o ! -f "$1" ];then
 +
 
     usage
 
     usage
 
     exit 1
 
     exit 1
 
fi
 
fi
 +
 
# Declare array
 
# Declare array
 
declare -a map
 
declare -a map
Line 473: Line 483:
 
index=0
 
index=0
 
while read -a array -u 7 ;do
 
while read -a array -u 7 ;do
 +
 
     count=${#array[@]}
 
     count=${#array[@]}
 
     # Traverse Each Word
 
     # Traverse Each Word
Line 478: Line 489:
 
echo "Total $index words Found"
 
echo "Total $index words Found"
 
count=$index
 
count=$index
 +
 
# Print Frequency
 
# Print Frequency
for((i=0;i<$count;i++));do
+
for((i=0;i<$count;i++));do echo -n "${map[$i]}:"
    echo -n "${map[$i]}:"
+
 
 
     elem=${map[$i]}
 
     elem=${map[$i]}
 
     # Indirect Reference to word's frequency
 
     # Indirect Reference to word's frequency
     echo $(eval "echo \$$elem")
+
     echo $(eval "echo \$elem")
 
done
 
done
  
Line 523: Line 535:
 
This script is just an example of indirect reference and is very simple and will not handle many words for example if any of the variable ecount, map, count etc are in the input then it will not work correctly also it is not able to handle any special characters. But with little effort it can be made to handle all the cases this can be a good learning exercise.
 
This script is just an example of indirect reference and is very simple and will not handle many words for example if any of the variable ecount, map, count etc are in the input then it will not work correctly also it is not able to handle any special characters. But with little effort it can be made to handle all the cases this can be a good learning exercise.
  
+
 
 
Networking
 
Networking
  
Line 535: Line 547:
 
     /dev/tcp/host/port
 
     /dev/tcp/host/port
 
     If host is a valid host name or Internet address, and port is an integer port number or service name, bash attempts to open a TCP connection to the corresponding socket.
 
     If host is a valid host name or Internet address, and port is an integer port number or service name, bash attempts to open a TCP connection to the corresponding socket.
 
 
     /dev/udp/host/port
 
     /dev/udp/host/port
 
     If host is a valid host name or Internet address, and port is an integer port number or service name, bash attempts to open a UDP connection to the corresponding socket.
 
     If host is a valid host name or Internet address, and port is an integer port number or service name, bash attempts to open a UDP connection to the corresponding socket.
  
 
  
 
For example to get home page of Google in your script, without using any other tool we can use following commands.
 
For example to get home page of Google in your script, without using any other tool we can use following commands.
Line 568: Line 578:
 
To know more about it read necat. Apart from necat wget and curl are also great tools to which are used used inside the scripts for handling networking.
 
To know more about it read necat. Apart from necat wget and curl are also great tools to which are used used inside the scripts for handling networking.
  
+
 
 
Single Instance Programs (.lock File)
 
Single Instance Programs (.lock File)
  
Line 575: Line 585:
 
Lets create a script which allows only single instance to run.
 
Lets create a script which allows only single instance to run.
  
#!/bin/bash
+
# !/bin/bash
 
# single_instance [-n]
 
# single_instance [-n]
 
# If -n option given Kill
 
# If -n option given Kill
Line 584: Line 594:
  
 
# An Array for temporary files
 
# An Array for temporary files
 +
 
declare -a temp_files
 
declare -a temp_files
  
 
# Add Temporary files in the global array
 
# Add Temporary files in the global array
 +
 
function add_file()
 
function add_file()
 
{
 
{
 +
 
     local index=${#temp_file}
 
     local index=${#temp_file}
 
     if [ ! -z "$1" -a -f "$1" ];then
 
     if [ ! -z "$1" -a -f "$1" ];then
Line 597: Line 610:
  
 
# Perform the cleanup and exit
 
# Perform the cleanup and exit
 +
 
function cleanup_and_exit()
 
function cleanup_and_exit()
 
{
 
{
 +
 
     local count=${#temp_file}
 
     local count=${#temp_file}
     for((i=0;i<count;i++))
+
     for((i=0;i<count;i++))   {
    {
 
 
         rm -rf ${temp_files[$i]} >/dev/null 2>&1
 
         rm -rf ${temp_files[$i]} >/dev/null 2>&1
 
     }
 
     }
Line 608: Line 622:
  
 
# Check if instance of scipt is already running
 
# Check if instance of scipt is already running
 +
 
function check_single_instance()
 
function check_single_instance()
 
{
 
{
 +
 
     # Check Lock File
 
     # Check Lock File
 
     if [ -f ${LOCK_FILE} ];then
 
     if [ -f ${LOCK_FILE} ];then
Line 618: Line 634:
  
 
# Add Signal Handler for SIGTERM
 
# Add Signal Handler for SIGTERM
 +
 
trap cleanup_and_exit SIGTERM
 
trap cleanup_and_exit SIGTERM
  
 
# Check if instance of this script is running
 
# Check if instance of this script is running
 +
 
if check_single_instance ;then
 
if check_single_instance ;then
 +
 
     echo "Instance is running"
 
     echo "Instance is running"
 
     if [ ! -z "$1" -a $1 = "-n" ];then
 
     if [ ! -z "$1" -a $1 = "-n" ];then
 
         # Kill Current Process
 
         # Kill Current Process
         read pid <<<$(cat ${PID_FILE})
+
         read pid <<<$(cat ${PID_FILE})           if [ ! -z "$pid" ];then
            if [ ! -z "$pid" ];then
 
 
             # Send SIGTERM
 
             # Send SIGTERM
 
             kill $pid
 
             kill $pid
Line 639: Line 657:
  
 
# Create Lock File
 
# Create Lock File
 +
 
touch ${LOCK_FILE}
 
touch ${LOCK_FILE}
  
 
# Create PID File
 
# Create PID File
 +
 
echo $$ > ${PID_FILE}
 
echo $$ > ${PID_FILE}
  
 
# Add files for Clean Up
 
# Add files for Clean Up
 +
 
add_file "${LOCK_FILE}"
 
add_file "${LOCK_FILE}"
 
add_file "${PID_FILE}"
 
add_file "${PID_FILE}"
  
 
# Add Your Script Functionality here
 
# Add Your Script Functionality here
 +
 
while true;
 
while true;
 +
 
     echo "This is an example of single Instance Script"
 
     echo "This is an example of single Instance Script"
 
     sleep 1;
 
     sleep 1;
Line 656: Line 679:
 
In the next part of this tutorial series we will use these techniques and create some working programs.
 
In the next part of this tutorial series we will use these techniques and create some working programs.
  
+
 
 
REFERENCES
 
REFERENCES
  
Line 693: Line 716:
 
Website
 
Website
  
Complete this CAPTCHA
+
Complete this CAPTCHA <span class="">'single_linebreak' style='background-color:lightgray'>¶</span> five − 1
five − 1 =
 
  
 
Notify me of follow-up comments by email.
 
Notify me of follow-up comments by email.

Revision as of 12:06, 16 November 2017

Use our feedback form to suggest a Linux related topic and we will publish it for you Click here to suggest

   Home
   About MyLinuxBook
   Authors needed
   Contact us
   Privacy Policy

MyLinuxBook

   C programming
   Linux administration
   Linux concepts
   Linux Developement
   Scripting
   Ubuntu
   Video

Home Bash scripting Bash shell scripting – Part II Bash shell scripting – Part II 5 days ago by Rajeev 0

In the first part of this article series on shell scripting we covered basics of what shell scripting is and how we can use it like other programming languages to automate our work. Now we are going to cover some more advanced technique such as arrays, functions, networking etc which makes these shell scripts much more than just bunch of commands. A re-look at shell scripting part-1

In last article we saw some basics of bash variable, how we can create variables assign values to them and retrieve back their values. Bash also support some operation on these variables. Here are some of the operations that can be performed on them.

var=”I am test string. You can run many operations on me”

   String Length

$ echo ${#var}

Output: 51

Substring Operations

   From given index

$ echo ${var:4}

Output: test string. You can run many operations on me

   From given index of given length

$ echo ${var:4:5}

Output: test

   Remove Suffix Pattern

$ echo ${var%%.*}

Output: I am test string

   Remove Prefix Pattern

$ echo ${var##*.}

Output: You can run many operations on me

   Substitution

$ echo ${var/on me/on this string}

Output: I am test string. You can run many operations on this string

Case Modification

We can specify a pattern after operator ( ^^ ,, and ~ ) to run this operation on part of string only.

   To Uppercase

$ echo ${var^^}

Output: I AM TEST STRING. YOU CAN RUN MANY OPERATIONS ON ME

   To Lowercase

$ echo ${var,,}

Output: i am test string. you can run many operations on me

   To Title Case

$ echo ${var~}

Output: i Am Test String. you Can Run Many Operations On Me

Parsing Command Line Options

We have seen how we can pass command line arguments to our scripts. But if we want to support the POSIX style command line options that almost all the tools available in Linux follow i.e. the character ‘-’ followed by one or more characters with these options clubbed together or passed separately. then bash also provide tool to parse these options..

e.g.

$ls -la /home $ ls -l -a /home

Both the above commands have same results and telling the ls program to provide output in list order and include all the files in the output.

Bash also provide tools to parse these POSIX style command line options. Bash built in command getopts is used to parse these arguments.

It is used as follows.

getopts OPTSTRING NAME [ARGS...]

OPTSRING : This is a list of all possible options your script supports. If any option requires value it is followed by a colon. e.g. “vhf:r”. Here it will parse option -v , -h -r, and -f option will need a value. NAME : Name stores the option that is found. ARG : If not defined “$@” is used otherwise we can specify a custom string to be parsed.


Here is a list of variables used by Getopts for parsing

OPTIND : This is the index of the next argument to be processed. If can set it to 1 to start parsing again. OPTARG : It stores the value of the option. e.g. -f file.txt , here file.txt will be saved in OPTARG. OPTERR : It can have two values If its value is 0 error messages will no be displayed. If its value is 1 error messages will be shown.


Error Reporting

If the first character of OPTSTRING is a colon, getopts uses silent error reporting otherwise it is verbose error mode

Silent Mode : In this mode, no error messages are printed. If an invalid option is seen, getopts places the option character found into OPTARG. If a required argument is not found, getopts places a ‘:’ into NAME and sets OPTARG to the option character found. Verbose Mode : If invalid option is seen, getopts places ‘?’ into NAME and unsets OPTARG. If a required argument is not found, a ‘?’ is placed in NAME, OPTARG is unset, and a diagnostic message is printed.

  1.  !/bin/bash
  2. cmd.sh -v -r -f

while getopts ":vf:r" opt;do

   case $opt in
       v)
           echo "Opion v found"
       ;;
       f)
           echo "Opion f found with value $OPTARG"
       ;;
       r)
           echo "Opion r found"
       ;;
       : )
           echo "Option $OPTARG requires an argument." >&2
       \?)
           echo "Invalid option: -$OPTARG"
       ;;
   esac

done

There is one limitation with getopts that it can not handle long option name like -abc or –option. To handle long option names there is another tool getopt (note missing ‘s’) which is an external tool.


Arrays

Arrays are the variables which holds multiple values. Bash support single dimensional array of data which may have same or different types. There is no limit on the length of the array, the index in the array does not need to be continuous, there can be missing indexes, their index does not need to be start from 0. Creating An Array

There are more than one way to create array in bash shell.

Creating Array beforehand

We can create array using the (). All the elements need to be separated by space, to specify a combined word use quotes to club them together.

month_names=( "None", "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")

The first element is placed at the index 0 and rest follows. There are no holes in the array. Notice that we need to use None at position 0 to shift all the values right.

Creating array on the run

You can directly assign any value to any index in the array. This way we can create any value at any index, we can create miss any index if we want to.

month_names[1]="Jan";

month_names[2]= "Feb";

month_names[3]= "Mar";month_names[4]= "Apr" month_names[5]= "May";month_names[6]= "Jun";

month_names[7]= "Jul";month_names[8]= "Aug" month_names[9]= "Sep";month_names[10]= "Oct";

month_names[11]= "Nov";month_names[12]= "Dec"

Notice we missed the index 0.

Declaring Array

We can also declare empty array using the keyword declare. declare -a months_array This creates an empty array with no values in it.


Accessing Array Elements

To access array use ${array[index]}

   Print Whole array

$ echo ${month_names[@]}

   Print one value

$ echo ${month_names[1]}

   Length of Array

$ echo ${#month_names[@]}

Traversing an array

   Traverse each value

for i in ${month_names[@]};do

   echo $i 

done

In this way if there is a value at some index in the array which have space in it that it will be considered as two separate strings.

   Traverse each index

for ((i=0;i<${#month_names[@]};i++));do

   echo ${month_names[$i]}

done

Functions

In higher programming languages functions or subroutines are set of statements, they have a name which can be used in the code instead of original statements. They helps in reducing the redundancy in code and also makes the code maintainable. In Shell Scripting we can consider these functions as other shell scripts which runs in the same shell. They can access Environment Variables which are persistent and can create new variables, overall they behave like the internal shell commands. Create Functions

As the scripts are not compiled but interpreted so it is necessary to create a function before using it. We can use functions in a script as well as the command line. In bash we can create a function by using the keyword function. function print() { echo -ne “$*” } This function will print its arguments and will not append a line break (-n option) and will also parse special characters (-e). You can see the commonly used functions defined in .bashrc or .bash_functions. Using Functions

Once we have created these functions in our script we can use them same as any other command.

$ print 1 2 3 4 $ print “Test” Command Line Arguments

Creating functions helps but it will not be so useful if we can not pass the arguments to our functions. Like the other commands there are no limits imposed on the number or arguments passed to a function, we can pass as many as we like. To handle these arguments in function we use them same way as we handle the command line arguments in a script, using $1, $2 $3 and so on, with one change that $0 remains the command you used to call your script and not the name of your function. Returning From Function

Functions perform on the given input and return a output. There are two methods with which functions in bash cab return a value to its caller.

Return Statement

Like the return statement of other programming language you can use the return statement to return a value. Return statement sets the exit status of the function, as this can only be a numerical value so there is this limitation that it can only be integer value. The value returned can be accessed by the special variable ?.

Echo or Printf

There is one another way to return a value to its caller as used by the script we can print the value using echo or printf and then retrieve it in the caller function using $() construct or back-tick. Stack Trace

If the $0 is the name of the script then how do we get the name of function we are in ? The default Bash array FUNCNAME is the stack of functions called. The value at index 0 is the name of current function. We can even get the depth of stack we are in by using ${#FUNCNAME[@]}.

Lets implement a stack using functions and array, we can add these function in .bashrc.

  1. Bash Stack Implementation using functions and array

declare -a __STACK__

__STACK_TOP__=0

function push() {

   __STACK__[$__STACK_TOP__]="$*"
   __STACK_TOP__=$((__STACK_TOP__+1))
   return 0

}

function pop() {

   if [ $__STACK_TOP__ -gt 0  ];then
       __STACK_TOP__=$((__STACK_TOP__-1))
       echo "${__STACK__[$__STACK_TOP__]}"
       unset __STACK__[$__STACK_TOP__]
       return 0
   fi
   return 1

}

function peek() {

   if [ $__STACK_TOP__ -gt 0  ];then
       echo "${__STACK__[$((__STACK_TOP__-1))]}"
       return 0
   fi
   return 1

}

We can enter these functions in our bashrc file and then use them as regular commands. Apart from performing push, pop, and peek operation these functions are also returning the success or failure status, which can be accessed by $?. Local Variables

Unlike other programming languages all the variables you create inside a function can be accessed outside of its boundaries. By default all the variables are available to whole script. But if we need function local variable which is not accessible outside of the function we can use the local keyword.

local var

This statement inside a function will create a function local variable.


Exceptions or Traps

Most of the time our scripts are handling opened file which needs to be flushed before the scripts exits. There are some temporary files which needs to be do deleted, overall sometimes scripts needs to do some cleanup work before it exits. To handle these cases trap command creates signal handler which will be invoked when a particular signal is received.

$ trap command list of signals

command can be function some script or a simple command.

  1.  !/bin/bash
  2. trap example

function runme {

   echo "Signal received" exit 0

}

trap runme SIGINT SIGTERM

while true; do

   sleep 1

done

Input Output

We have used read, echo and printf for input output in our bash script. We have also used the redirection to modify the default input output stream. Lets see some more advanced input output methods provided by bash shell. Bash shell provide methods to handle file descriptors, open new file descriptors, redirect one in another and write in file opened with given file descriptor. By default Three file descriptors are opened for any program in linux.

   File descriptor 0 is stdin or standard input
   File descriptor 1 is stdout which is standard output
   File descriptor 2 is stderr which is standard error device

Default all these are set as terminal for terminal programs, when we use the redirection the shell opens the file and redirect the input/output by duplicating the file descriptors. Same way we can open other files and manually redirect output, input and/or error to that file. Open and Close File descriptors

The command exec is used to open and close file descriptor.

$ exec 5 <>file.txt This command will open file file.txt and assign the file descriptor 5.

$ exec 5>&-

This command will close the file descriptor. Redirection

To handle redirection we can use following format m>&n Redirect output of file descriptor m (ms is default to 1) to file decrioptor n m<&n Redirect input of file descriptor m ( ms is defualt to 0 ) to file decrioptor n Redirecting Standard Output to file descriptor

$ date >&5

This command will redirect the output of date command to file descriptor 5.

Redirecting Standard Input from file descriptor

$ read line

This command will read the first line from file descriptor 5.

Redirecting Standard Error As the standard error is just a output stream with file descriptor 2 we can easily redirect it to some other file decsriptor.

$ ls file.txt 2>&5

This command will redirect the standard error of ls to file descriptor 5. Read Command Revisited

We have used the read command for taking input from terminal or reading a file using redirection, lets see some more stuffs that can be done with this command.

Read from file descriptor

read command can be used to read from a given file descriptor.

$ read line -u 5

Reading Multiple Values

We can use read command to read multiple variables at once

$ read a b c

Read Array From stdin

Read command can also be used to read an array from stdin and assign the values to successive indices in an array

$ read -a array

Inter Process Communication(IPC) with Named Pipe or FIFO

IPC or inter process communication is very essential when we want to distribute the jobs among various processes. We have already seen the use of pipe which is used to communicate between parent and child process. But If the processes are not related we can still communicate by using the named pipe or fifo. Pipes and Fifo are nothing but the redirection of the data streams We can create a named pipe of Fifo using the command mknod or mkfifo.

$ mkfifo /tmp/test

This command will create a fifo file in /tmp directory.

We can see the file by using command

$ ls -la /tmp/test prw-r--r-- 1 sherill hadmin 0 Dec 10 16:24 /tmp/test|

The initial p character shows its a fifo file.

Now open two terminals on one type

$ read line < /tmp/test This read will blocked until it read something from fifo file.

In another terminal enter command

$ echo "Fifo Example" > /temp/test

This command will enter text in the fifo, and the read command on previous terminal will be completed. Now if you type echo $line on the previous terminal you will see that the data has successfully being read.

If you run the same command again the read will wait for the data, as the Fifo is empty due to last read.

We can use a timeout with read to stop hanging infinitely on the input.

for example

exec 7<> /tmp/testwhile true;do

   read -t0 -u7 line
   if [ $? -eq 0 ];then
       echo "$line is read"
       # Do something
   else
       echo " No Data. Sleep"
       # Do something else
   fi

done

Use of fifo

We have seen how we can use the redirection to redirect the input or output to or from a program using pipe. But this works only in one way i.e. either you redirect input from the command or you redirect output to that command. If we need to redirect the input to a program and along with its output to be read by same program then we can do this using fifo. For example here we want to redirect output from netcat to shell and then read the output of this shell back so in netcat. So we created a fifo and redirected the output of netcat command to fifo. Then we are reading this fifio using cat and redirectin it to shell which process the data and then its output is redirected to netcat again.

$ mkfifo /tmp/myfifo $ cat /tmp/myfifo | /bin/sh | nc -l 1356 > /tmp/myfifo

This simple command will produce a netcat server running a shell which can be accesed using command

$ netcat localhost 1356

If you want to access the shell from a remote machine just change the localhost to the IP address of the machine and you get yourself a remote shell.


Indirect Reference

We can easily reference a variable value with $variable syntax, but if we need value of a value .i.e indirect reference. Bash also supports the indirect references with the help of eval command and \$$variable syntax. e.g.

x=10 y=x

echo $(eval echo \$$y)

We can use indirect references to create a look up table.

Lets create a program which will print the frequency of each word.

  1.  !/bin/bash
  2.  !/bin/bash
  3. word_freq

function usage {

   echo "usage: $0"

}

  1. Check argument is given and it is a file

if [ -z "$1" -o ! -f "$1" ];then

   usage
   exit 1

fi

  1. Declare array

declare -a map

  1. Open File

exec 7<> "$1" index=0 while read -a array -u 7 ;do

   count=${#array[@]}
   # Traverse Each Word
   for((i=0;i&-

echo "Total $index words Found" count=$index

  1. Print Frequency

for((i=0;i<$count;i++));do echo -n "${map[$i]}:"

   elem=${map[$i]}
   # Indirect Reference to word's frequency
   echo $(eval "echo \$elem")

done

I ran this script with following input.

$ cat input.txt This is an input to word frequency script This script will occurrence the frequency of each word and report back This script uses bash indirect references to create a Map and uses it to save occurrence

$ ./word_freq input.txt Total 25 words Found This:3 is:1 an:1 input:1 to:3 word:2 infrequence script:3 will:1 occurrence:2 the:1 of:1 each:1 and:2 report:1 back:1 uses:2 bash:1 indirect:1 references:1 create:1 a:1 Map:1 it:1 save:1

This script is just an example of indirect reference and is very simple and will not handle many words for example if any of the variable ecount, map, count etc are in the input then it will not work correctly also it is not able to handle any special characters. But with little effort it can be made to handle all the cases this can be a good learning exercise.


Networking

There are two ways to use networking in your scripts Bash internal socket handling

As we know all devices in linux are files, we can open then using open system call and can do basic input output. This can also be done with devices /dev/cp and /dev/udp.

From the bash manpage:

   /dev/tcp/host/port
   If host is a valid host name or Internet address, and port is an integer port number or service name, bash attempts to open a TCP connection to the corresponding socket.
   /dev/udp/host/port
   If host is a valid host name or Internet address, and port is an integer port number or service name, bash attempts to open a UDP connection to the corresponding socket.


For example to get home page of Google in your script, without using any other tool we can use following commands.

$ exec 7/dev/tcp/mylinuxbook.com/80 $ echo -en "GET / HTTP/1.0\r\nHost: mylinuxbook.com\r\nConnection: close\r\n\r\n">&7 $ cat &-

In the first line we are opening a connection with the server mylinuxbook.com at port 80 with the file descriptor 7, then we sent a Get request for Home page using HTTP protocol. Then are reading the server response using cat command. At last when its done we close the connection using exec command. Using External Tools

Depending on need there are numerous tools available on linux to choose from. One among them is Netcat. Which is also called “Swiss-army knife” of networking which can be used to create TCP as well as UDP connections.

Lets create a simple time server using netcat.

At server (172.31.100.7) enter command

$ while true; do date | nc -u -l 13 ; done

This command will create a udp server listening at port 13. When any machine connects it will return the output of date command. And then exit. While loop is to create the server again.

Now to get time at any machine enter command

$ nc -u 172.31.100.7 13

This command will connect to serer 172.31.100.7 at the port 13 and retrieve print the data received from the server

To know more about it read necat. Apart from necat wget and curl are also great tools to which are used used inside the scripts for handling networking.


Single Instance Programs (.lock File)

.lock files are used to constrained our script to run in single instance mode, specially in the case when multiple instance can corrupt the system state like aptd trying to install a software or updating. If multiple aptd are allowed to run in parallel they may corrupt the database. Usually lock files are present in the /var/lock or /tmp directory.

Lets create a script which allows only single instance to run.

  1.  !/bin/bash
  2. single_instance [-n]
  3. If -n option given Kill

APP_NAME=single_instance DIR=/tmp LOCK_FILE=${DIR}/${APP_NAME}.lock PID_FILE=${DIR}/${APP_NAME}.pid

  1. An Array for temporary files

declare -a temp_files

  1. Add Temporary files in the global array

function add_file() {

   local index=${#temp_file}
   if [ ! -z "$1" -a -f "$1" ];then
       index=$((index+1))
       temp_files[$index]="$1"
   fi

}

  1. Perform the cleanup and exit

function cleanup_and_exit() {

   local count=${#temp_file}
   for((i=0;i<count;i++))    {
       rm -rf ${temp_files[$i]} >/dev/null 2>&1
   }
   exit 0

}

  1. Check if instance of scipt is already running

function check_single_instance() {

   # Check Lock File
   if [ -f ${LOCK_FILE} ];then
       return 1
   fi
   return 0

}

  1. Add Signal Handler for SIGTERM

trap cleanup_and_exit SIGTERM

  1. Check if instance of this script is running

if check_single_instance ;then

   echo "Instance is running"
   if [ ! -z "$1" -a $1 = "-n" ];then
       # Kill Current Process
       read pid <<<$(cat ${PID_FILE})            if [ ! -z "$pid" ];then
           # Send SIGTERM
           kill $pid
           while kill -0 $pid;do
           echo "Waiting for $pid to exit"
           done
   fi
   else
       exit 0
   fi

fi

  1. Create Lock File

touch ${LOCK_FILE}

  1. Create PID File

echo $$ > ${PID_FILE}

  1. Add files for Clean Up

add_file "${LOCK_FILE}" add_file "${PID_FILE}"

  1. Add Your Script Functionality here

while true;

   echo "This is an example of single Instance Script"
   sleep 1;

done

In the next part of this tutorial series we will use these techniques and create some working programs.


REFERENCES

Advanced Bash Scripting

Bash TCP IP

Getopts Share this:

   LinkedIn
   Reddit
   StumbleUpon
   Digg
   Tumblr
   Pinterest

Tags: bash scripting, bash shell scripting, Linux scripting, Linux shell, shell scripting

Rajeev Get Updates

Subscribe to our e-mail newsletter to receive updates. Share This Post Related Articles

   Bash shell scripting – Part I
   Linux Shell environment

Leave a Reply

Name (Required)

Mail (will not be published) (Required)

Website

Complete this CAPTCHA 'single_linebreak' style='background-color:lightgray'>¶ five − 1

Notify me of follow-up comments by email.

Notify me of new posts by email. ©2013 MyLinuxBook- All rights reserved Subscribe to our new posts and monthly Newsletter

To subscribe to our new posts and monthly newsletter simply add your email below. A confirmation email will be sent to you!

© 2013 MyLinuxBook. All rights reserved. Site Admin · Entries RSS · Comments RSS Powered by WordPress · Designed by Theme Junkie

Internal error - GIGA Information Board

Difference between revisions of "Bash Tips"

(Created page with "Use our feedback form to suggest a Linux related topic and we will publish it for you Click here to suggest Home About MyLinuxBook Authors needed Contact us...")
 
Line 109: Line 109:
 
ARG : If not defined “$@” is used otherwise we can specify a custom string to be parsed.
 
ARG : If not defined “$@” is used otherwise we can specify a custom string to be parsed.
  
+
 
  
 
Here is a list of variables used by Getopts for parsing
 
Here is a list of variables used by Getopts for parsing
Line 117: Line 117:
 
OPTERR : It can have two values If its value is 0 error messages will no be displayed. If its value is 1 error messages will be shown.
 
OPTERR : It can have two values If its value is 0 error messages will no be displayed. If its value is 1 error messages will be shown.
  
+
 
  
 
Error Reporting
 
Error Reporting
Line 126: Line 126:
 
Verbose Mode : If invalid option is seen, getopts places ‘?’ into NAME and unsets OPTARG. If a required argument is not found, a ‘?’ is placed in NAME, OPTARG is unset, and a diagnostic message is printed.
 
Verbose Mode : If invalid option is seen, getopts places ‘?’ into NAME and unsets OPTARG. If a required argument is not found, a ‘?’ is placed in NAME, OPTARG is unset, and a diagnostic message is printed.
  
#!/bin/bash
+
# !/bin/bash
# cmd.sh -v -r -f <text>
+
# cmd.sh -v -r -f
 
while getopts ":vf:r" opt;do
 
while getopts ":vf:r" opt;do
 +
 
     case $opt in
 
     case $opt in
 
         v)
 
         v)
Line 149: Line 150:
 
There is one limitation with getopts that it can not handle long option name like -abc or –option. To handle long option names there is another tool getopt (note missing ‘s’) which is an external tool.
 
There is one limitation with getopts that it can not handle long option name like -abc or –option. To handle long option names there is another tool getopt (note missing ‘s’) which is an external tool.
  
+
 
 
Arrays
 
Arrays
  
Line 185: Line 186:
 
We can also declare empty array using the keyword declare. declare -a months_array This creates an empty array with no values in it.
 
We can also declare empty array using the keyword declare. declare -a months_array This creates an empty array with no values in it.
  
+
 
 
Accessing Array Elements
 
Accessing Array Elements
  
Line 207: Line 208:
  
 
for i in ${month_names[@]};do
 
for i in ${month_names[@]};do
 +
 
     echo $i  
 
     echo $i  
 
done
 
done
Line 215: Line 217:
  
 
for ((i=0;i<${#month_names[@]};i++));do
 
for ((i=0;i<${#month_names[@]};i++));do
 +
 
     echo ${month_names[$i]}
 
     echo ${month_names[$i]}
 
done
 
done
Line 258: Line 261:
 
function push()
 
function push()
 
{
 
{
 +
 
     __STACK__[$__STACK_TOP__]="$*"
 
     __STACK__[$__STACK_TOP__]="$*"
 
     __STACK_TOP__=$((__STACK_TOP__+1))
 
     __STACK_TOP__=$((__STACK_TOP__+1))
Line 265: Line 269:
 
function pop()
 
function pop()
 
{
 
{
 +
 
     if [ $__STACK_TOP__ -gt 0  ];then
 
     if [ $__STACK_TOP__ -gt 0  ];then
 
         __STACK_TOP__=$((__STACK_TOP__-1))
 
         __STACK_TOP__=$((__STACK_TOP__-1))
Line 276: Line 281:
 
function peek()
 
function peek()
 
{
 
{
 +
 
     if [ $__STACK_TOP__ -gt 0  ];then
 
     if [ $__STACK_TOP__ -gt 0  ];then
 
         echo "${__STACK__[$((__STACK_TOP__-1))]}"
 
         echo "${__STACK__[$((__STACK_TOP__-1))]}"
Line 292: Line 298:
 
This statement inside a function will create a function local variable.
 
This statement inside a function will create a function local variable.
  
+
 
 
Exceptions or Traps
 
Exceptions or Traps
  
Line 301: Line 307:
 
command can be function some script or a simple command.
 
command can be function some script or a simple command.
  
#!/bin/bash
+
# !/bin/bash
#trap example
+
# trap example
  
 
function runme
 
function runme
 
{
 
{
 +
 
     echo "Signal received" exit 0
 
     echo "Signal received" exit 0
 
}
 
}
Line 312: Line 319:
  
 
while true; do
 
while true; do
 +
 
     sleep 1
 
     sleep 1
 
done
 
done
Line 329: Line 337:
  
 
$ exec 5 <>file.txt
 
$ exec 5 <>file.txt
 
 
This command will open file file.txt and assign the file descriptor 5.
 
This command will open file file.txt and assign the file descriptor 5.
  
Line 338: Line 345:
  
 
To handle redirection we can use following format m>&n Redirect output of file descriptor m (ms is default to 1) to file decrioptor n m<&n Redirect input of file descriptor m ( ms is defualt to 0 ) to file decrioptor n
 
To handle redirection we can use following format m>&n Redirect output of file descriptor m (ms is default to 1) to file decrioptor n m<&n Redirect input of file descriptor m ( ms is defualt to 0 ) to file decrioptor n
 
 
Redirecting Standard Output to file descriptor
 
Redirecting Standard Output to file descriptor
  
Line 398: Line 404:
  
 
$ read line < /tmp/test
 
$ read line < /tmp/test
 
 
This read will blocked until it read something from fifo file.
 
This read will blocked until it read something from fifo file.
  
Line 413: Line 418:
 
for example
 
for example
  
exec 7<> /tmp/test
+
exec 7<> /tmp/testwhile true;do
while  true;do
+
 
 
     read -t0 -u7 line
 
     read -t0 -u7 line
 
     if [ $? -eq 0 ];then
 
     if [ $? -eq 0 ];then
Line 432: Line 437:
  
 
$ mkfifo /tmp/myfifo
 
$ mkfifo /tmp/myfifo
$ cat /tmp/myfifo | /bin/sh | nc -l 1356 > /tmp/myfifo
+
$ cat /tmp/myfifo | /bin/sh | nc -l 1356 > /tmp/myfifo
  
 
This simple command will produce a netcat server running a shell which can be accesed using command
 
This simple command will produce a netcat server running a shell which can be accesed using command
Line 440: Line 445:
 
If you want to access the shell from a remote machine just change the localhost to the IP address of the machine and you get yourself a remote shell.
 
If you want to access the shell from a remote machine just change the localhost to the IP address of the machine and you get yourself a remote shell.
  
+
 
 
Indirect Reference
 
Indirect Reference
  
Line 454: Line 459:
 
Lets create a program which will print the frequency of each word.
 
Lets create a program which will print the frequency of each word.
  
#!/bin/bash
+
# !/bin/bash
#!/bin/bash
+
# !/bin/bash
 
# word_freq
 
# word_freq
 +
 
function usage
 
function usage
 
{
 
{
 +
 
     echo "usage: $0"
 
     echo "usage: $0"
 
}
 
}
  
 
# Check argument is given and it is a file
 
# Check argument is given and it is a file
 +
 
if [ -z "$1" -o ! -f "$1" ];then
 
if [ -z "$1" -o ! -f "$1" ];then
 +
 
     usage
 
     usage
 
     exit 1
 
     exit 1
 
fi
 
fi
 +
 
# Declare array
 
# Declare array
 
declare -a map
 
declare -a map
Line 473: Line 483:
 
index=0
 
index=0
 
while read -a array -u 7 ;do
 
while read -a array -u 7 ;do
 +
 
     count=${#array[@]}
 
     count=${#array[@]}
 
     # Traverse Each Word
 
     # Traverse Each Word
Line 478: Line 489:
 
echo "Total $index words Found"
 
echo "Total $index words Found"
 
count=$index
 
count=$index
 +
 
# Print Frequency
 
# Print Frequency
for((i=0;i<$count;i++));do
+
for((i=0;i<$count;i++));do echo -n "${map[$i]}:"
    echo -n "${map[$i]}:"
+
 
 
     elem=${map[$i]}
 
     elem=${map[$i]}
 
     # Indirect Reference to word's frequency
 
     # Indirect Reference to word's frequency
     echo $(eval "echo \$$elem")
+
     echo $(eval "echo \$elem")
 
done
 
done
  
Line 523: Line 535:
 
This script is just an example of indirect reference and is very simple and will not handle many words for example if any of the variable ecount, map, count etc are in the input then it will not work correctly also it is not able to handle any special characters. But with little effort it can be made to handle all the cases this can be a good learning exercise.
 
This script is just an example of indirect reference and is very simple and will not handle many words for example if any of the variable ecount, map, count etc are in the input then it will not work correctly also it is not able to handle any special characters. But with little effort it can be made to handle all the cases this can be a good learning exercise.
  
+
 
 
Networking
 
Networking
  
Line 535: Line 547:
 
     /dev/tcp/host/port
 
     /dev/tcp/host/port
 
     If host is a valid host name or Internet address, and port is an integer port number or service name, bash attempts to open a TCP connection to the corresponding socket.
 
     If host is a valid host name or Internet address, and port is an integer port number or service name, bash attempts to open a TCP connection to the corresponding socket.
 
 
     /dev/udp/host/port
 
     /dev/udp/host/port
 
     If host is a valid host name or Internet address, and port is an integer port number or service name, bash attempts to open a UDP connection to the corresponding socket.
 
     If host is a valid host name or Internet address, and port is an integer port number or service name, bash attempts to open a UDP connection to the corresponding socket.
  
 
  
 
For example to get home page of Google in your script, without using any other tool we can use following commands.
 
For example to get home page of Google in your script, without using any other tool we can use following commands.
Line 568: Line 578:
 
To know more about it read necat. Apart from necat wget and curl are also great tools to which are used used inside the scripts for handling networking.
 
To know more about it read necat. Apart from necat wget and curl are also great tools to which are used used inside the scripts for handling networking.
  
+
 
 
Single Instance Programs (.lock File)
 
Single Instance Programs (.lock File)
  
Line 575: Line 585:
 
Lets create a script which allows only single instance to run.
 
Lets create a script which allows only single instance to run.
  
#!/bin/bash
+
# !/bin/bash
 
# single_instance [-n]
 
# single_instance [-n]
 
# If -n option given Kill
 
# If -n option given Kill
Line 584: Line 594:
  
 
# An Array for temporary files
 
# An Array for temporary files
 +
 
declare -a temp_files
 
declare -a temp_files
  
 
# Add Temporary files in the global array
 
# Add Temporary files in the global array
 +
 
function add_file()
 
function add_file()
 
{
 
{
 +
 
     local index=${#temp_file}
 
     local index=${#temp_file}
 
     if [ ! -z "$1" -a -f "$1" ];then
 
     if [ ! -z "$1" -a -f "$1" ];then
Line 597: Line 610:
  
 
# Perform the cleanup and exit
 
# Perform the cleanup and exit
 +
 
function cleanup_and_exit()
 
function cleanup_and_exit()
 
{
 
{
 +
 
     local count=${#temp_file}
 
     local count=${#temp_file}
     for((i=0;i<count;i++))
+
     for((i=0;i<count;i++))   {
    {
 
 
         rm -rf ${temp_files[$i]} >/dev/null 2>&1
 
         rm -rf ${temp_files[$i]} >/dev/null 2>&1
 
     }
 
     }
Line 608: Line 622:
  
 
# Check if instance of scipt is already running
 
# Check if instance of scipt is already running
 +
 
function check_single_instance()
 
function check_single_instance()
 
{
 
{
 +
 
     # Check Lock File
 
     # Check Lock File
 
     if [ -f ${LOCK_FILE} ];then
 
     if [ -f ${LOCK_FILE} ];then
Line 618: Line 634:
  
 
# Add Signal Handler for SIGTERM
 
# Add Signal Handler for SIGTERM
 +
 
trap cleanup_and_exit SIGTERM
 
trap cleanup_and_exit SIGTERM
  
 
# Check if instance of this script is running
 
# Check if instance of this script is running
 +
 
if check_single_instance ;then
 
if check_single_instance ;then
 +
 
     echo "Instance is running"
 
     echo "Instance is running"
 
     if [ ! -z "$1" -a $1 = "-n" ];then
 
     if [ ! -z "$1" -a $1 = "-n" ];then
 
         # Kill Current Process
 
         # Kill Current Process
         read pid <<<$(cat ${PID_FILE})
+
         read pid <<<$(cat ${PID_FILE})           if [ ! -z "$pid" ];then
            if [ ! -z "$pid" ];then
 
 
             # Send SIGTERM
 
             # Send SIGTERM
 
             kill $pid
 
             kill $pid
Line 639: Line 657:
  
 
# Create Lock File
 
# Create Lock File
 +
 
touch ${LOCK_FILE}
 
touch ${LOCK_FILE}
  
 
# Create PID File
 
# Create PID File
 +
 
echo $$ > ${PID_FILE}
 
echo $$ > ${PID_FILE}
  
 
# Add files for Clean Up
 
# Add files for Clean Up
 +
 
add_file "${LOCK_FILE}"
 
add_file "${LOCK_FILE}"
 
add_file "${PID_FILE}"
 
add_file "${PID_FILE}"
  
 
# Add Your Script Functionality here
 
# Add Your Script Functionality here
 +
 
while true;
 
while true;
 +
 
     echo "This is an example of single Instance Script"
 
     echo "This is an example of single Instance Script"
 
     sleep 1;
 
     sleep 1;
Line 656: Line 679:
 
In the next part of this tutorial series we will use these techniques and create some working programs.
 
In the next part of this tutorial series we will use these techniques and create some working programs.
  
+
 
 
REFERENCES
 
REFERENCES
  
Line 693: Line 716:
 
Website
 
Website
  
Complete this CAPTCHA
+
Complete this CAPTCHA <span class="">'single_linebreak' style='background-color:lightgray'>¶</span> five − 1
five − 1 =
 
  
 
Notify me of follow-up comments by email.
 
Notify me of follow-up comments by email.

Revision as of 12:06, 16 November 2017

Use our feedback form to suggest a Linux related topic and we will publish it for you Click here to suggest

   Home
   About MyLinuxBook
   Authors needed
   Contact us
   Privacy Policy

MyLinuxBook

   C programming
   Linux administration
   Linux concepts
   Linux Developement
   Scripting
   Ubuntu
   Video

Home Bash scripting Bash shell scripting – Part II Bash shell scripting – Part II 5 days ago by Rajeev 0

In the first part of this article series on shell scripting we covered basics of what shell scripting is and how we can use it like other programming languages to automate our work. Now we are going to cover some more advanced technique such as arrays, functions, networking etc which makes these shell scripts much more than just bunch of commands. A re-look at shell scripting part-1

In last article we saw some basics of bash variable, how we can create variables assign values to them and retrieve back their values. Bash also support some operation on these variables. Here are some of the operations that can be performed on them.

var=”I am test string. You can run many operations on me”

   String Length

$ echo ${#var}

Output: 51

Substring Operations

   From given index

$ echo ${var:4}

Output: test string. You can run many operations on me

   From given index of given length

$ echo ${var:4:5}

Output: test

   Remove Suffix Pattern

$ echo ${var%%.*}

Output: I am test string

   Remove Prefix Pattern

$ echo ${var##*.}

Output: You can run many operations on me

   Substitution

$ echo ${var/on me/on this string}

Output: I am test string. You can run many operations on this string

Case Modification

We can specify a pattern after operator ( ^^ ,, and ~ ) to run this operation on part of string only.

   To Uppercase

$ echo ${var^^}

Output: I AM TEST STRING. YOU CAN RUN MANY OPERATIONS ON ME

   To Lowercase

$ echo ${var,,}

Output: i am test string. you can run many operations on me

   To Title Case

$ echo ${var~}

Output: i Am Test String. you Can Run Many Operations On Me

Parsing Command Line Options

We have seen how we can pass command line arguments to our scripts. But if we want to support the POSIX style command line options that almost all the tools available in Linux follow i.e. the character ‘-’ followed by one or more characters with these options clubbed together or passed separately. then bash also provide tool to parse these options..

e.g.

$ls -la /home $ ls -l -a /home

Both the above commands have same results and telling the ls program to provide output in list order and include all the files in the output.

Bash also provide tools to parse these POSIX style command line options. Bash built in command getopts is used to parse these arguments.

It is used as follows.

getopts OPTSTRING NAME [ARGS...]

OPTSRING : This is a list of all possible options your script supports. If any option requires value it is followed by a colon. e.g. “vhf:r”. Here it will parse option -v , -h -r, and -f option will need a value. NAME : Name stores the option that is found. ARG : If not defined “$@” is used otherwise we can specify a custom string to be parsed.


Here is a list of variables used by Getopts for parsing

OPTIND : This is the index of the next argument to be processed. If can set it to 1 to start parsing again. OPTARG : It stores the value of the option. e.g. -f file.txt , here file.txt will be saved in OPTARG. OPTERR : It can have two values If its value is 0 error messages will no be displayed. If its value is 1 error messages will be shown.


Error Reporting

If the first character of OPTSTRING is a colon, getopts uses silent error reporting otherwise it is verbose error mode

Silent Mode : In this mode, no error messages are printed. If an invalid option is seen, getopts places the option character found into OPTARG. If a required argument is not found, getopts places a ‘:’ into NAME and sets OPTARG to the option character found. Verbose Mode : If invalid option is seen, getopts places ‘?’ into NAME and unsets OPTARG. If a required argument is not found, a ‘?’ is placed in NAME, OPTARG is unset, and a diagnostic message is printed.

  1.  !/bin/bash
  2. cmd.sh -v -r -f

while getopts ":vf:r" opt;do

   case $opt in
       v)
           echo "Opion v found"
       ;;
       f)
           echo "Opion f found with value $OPTARG"
       ;;
       r)
           echo "Opion r found"
       ;;
       : )
           echo "Option $OPTARG requires an argument." >&2
       \?)
           echo "Invalid option: -$OPTARG"
       ;;
   esac

done

There is one limitation with getopts that it can not handle long option name like -abc or –option. To handle long option names there is another tool getopt (note missing ‘s’) which is an external tool.


Arrays

Arrays are the variables which holds multiple values. Bash support single dimensional array of data which may have same or different types. There is no limit on the length of the array, the index in the array does not need to be continuous, there can be missing indexes, their index does not need to be start from 0. Creating An Array

There are more than one way to create array in bash shell.

Creating Array beforehand

We can create array using the (). All the elements need to be separated by space, to specify a combined word use quotes to club them together.

month_names=( "None", "Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec")

The first element is placed at the index 0 and rest follows. There are no holes in the array. Notice that we need to use None at position 0 to shift all the values right.

Creating array on the run

You can directly assign any value to any index in the array. This way we can create any value at any index, we can create miss any index if we want to.

month_names[1]="Jan";

month_names[2]= "Feb";

month_names[3]= "Mar";month_names[4]= "Apr" month_names[5]= "May";month_names[6]= "Jun";

month_names[7]= "Jul";month_names[8]= "Aug" month_names[9]= "Sep";month_names[10]= "Oct";

month_names[11]= "Nov";month_names[12]= "Dec"

Notice we missed the index 0.

Declaring Array

We can also declare empty array using the keyword declare. declare -a months_array This creates an empty array with no values in it.


Accessing Array Elements

To access array use ${array[index]}

   Print Whole array

$ echo ${month_names[@]}

   Print one value

$ echo ${month_names[1]}

   Length of Array

$ echo ${#month_names[@]}

Traversing an array

   Traverse each value

for i in ${month_names[@]};do

   echo $i 

done

In this way if there is a value at some index in the array which have space in it that it will be considered as two separate strings.

   Traverse each index

for ((i=0;i<${#month_names[@]};i++));do

   echo ${month_names[$i]}

done

Functions

In higher programming languages functions or subroutines are set of statements, they have a name which can be used in the code instead of original statements. They helps in reducing the redundancy in code and also makes the code maintainable. In Shell Scripting we can consider these functions as other shell scripts which runs in the same shell. They can access Environment Variables which are persistent and can create new variables, overall they behave like the internal shell commands. Create Functions

As the scripts are not compiled but interpreted so it is necessary to create a function before using it. We can use functions in a script as well as the command line. In bash we can create a function by using the keyword function. function print() { echo -ne “$*” } This function will print its arguments and will not append a line break (-n option) and will also parse special characters (-e). You can see the commonly used functions defined in .bashrc or .bash_functions. Using Functions

Once we have created these functions in our script we can use them same as any other command.

$ print 1 2 3 4 $ print “Test” Command Line Arguments

Creating functions helps but it will not be so useful if we can not pass the arguments to our functions. Like the other commands there are no limits imposed on the number or arguments passed to a function, we can pass as many as we like. To handle these arguments in function we use them same way as we handle the command line arguments in a script, using $1, $2 $3 and so on, with one change that $0 remains the command you used to call your script and not the name of your function. Returning From Function

Functions perform on the given input and return a output. There are two methods with which functions in bash cab return a value to its caller.

Return Statement

Like the return statement of other programming language you can use the return statement to return a value. Return statement sets the exit status of the function, as this can only be a numerical value so there is this limitation that it can only be integer value. The value returned can be accessed by the special variable ?.

Echo or Printf

There is one another way to return a value to its caller as used by the script we can print the value using echo or printf and then retrieve it in the caller function using $() construct or back-tick. Stack Trace

If the $0 is the name of the script then how do we get the name of function we are in ? The default Bash array FUNCNAME is the stack of functions called. The value at index 0 is the name of current function. We can even get the depth of stack we are in by using ${#FUNCNAME[@]}.

Lets implement a stack using functions and array, we can add these function in .bashrc.

  1. Bash Stack Implementation using functions and array

declare -a __STACK__

__STACK_TOP__=0

function push() {

   __STACK__[$__STACK_TOP__]="$*"
   __STACK_TOP__=$((__STACK_TOP__+1))
   return 0

}

function pop() {

   if [ $__STACK_TOP__ -gt 0  ];then
       __STACK_TOP__=$((__STACK_TOP__-1))
       echo "${__STACK__[$__STACK_TOP__]}"
       unset __STACK__[$__STACK_TOP__]
       return 0
   fi
   return 1

}

function peek() {

   if [ $__STACK_TOP__ -gt 0  ];then
       echo "${__STACK__[$((__STACK_TOP__-1))]}"
       return 0
   fi
   return 1

}

We can enter these functions in our bashrc file and then use them as regular commands. Apart from performing push, pop, and peek operation these functions are also returning the success or failure status, which can be accessed by $?. Local Variables

Unlike other programming languages all the variables you create inside a function can be accessed outside of its boundaries. By default all the variables are available to whole script. But if we need function local variable which is not accessible outside of the function we can use the local keyword.

local var

This statement inside a function will create a function local variable.


Exceptions or Traps

Most of the time our scripts are handling opened file which needs to be flushed before the scripts exits. There are some temporary files which needs to be do deleted, overall sometimes scripts needs to do some cleanup work before it exits. To handle these cases trap command creates signal handler which will be invoked when a particular signal is received.

$ trap command list of signals

command can be function some script or a simple command.

  1.  !/bin/bash
  2. trap example

function runme {

   echo "Signal received" exit 0

}

trap runme SIGINT SIGTERM

while true; do

   sleep 1

done

Input Output

We have used read, echo and printf for input output in our bash script. We have also used the redirection to modify the default input output stream. Lets see some more advanced input output methods provided by bash shell. Bash shell provide methods to handle file descriptors, open new file descriptors, redirect one in another and write in file opened with given file descriptor. By default Three file descriptors are opened for any program in linux.

   File descriptor 0 is stdin or standard input
   File descriptor 1 is stdout which is standard output
   File descriptor 2 is stderr which is standard error device

Default all these are set as terminal for terminal programs, when we use the redirection the shell opens the file and redirect the input/output by duplicating the file descriptors. Same way we can open other files and manually redirect output, input and/or error to that file. Open and Close File descriptors

The command exec is used to open and close file descriptor.

$ exec 5 <>file.txt This command will open file file.txt and assign the file descriptor 5.

$ exec 5>&-

This command will close the file descriptor. Redirection

To handle redirection we can use following format m>&n Redirect output of file descriptor m (ms is default to 1) to file decrioptor n m<&n Redirect input of file descriptor m ( ms is defualt to 0 ) to file decrioptor n Redirecting Standard Output to file descriptor

$ date >&5

This command will redirect the output of date command to file descriptor 5.

Redirecting Standard Input from file descriptor

$ read line

This command will read the first line from file descriptor 5.

Redirecting Standard Error As the standard error is just a output stream with file descriptor 2 we can easily redirect it to some other file decsriptor.

$ ls file.txt 2>&5

This command will redirect the standard error of ls to file descriptor 5. Read Command Revisited

We have used the read command for taking input from terminal or reading a file using redirection, lets see some more stuffs that can be done with this command.

Read from file descriptor

read command can be used to read from a given file descriptor.

$ read line -u 5

Reading Multiple Values

We can use read command to read multiple variables at once

$ read a b c

Read Array From stdin

Read command can also be used to read an array from stdin and assign the values to successive indices in an array

$ read -a array

Inter Process Communication(IPC) with Named Pipe or FIFO

IPC or inter process communication is very essential when we want to distribute the jobs among various processes. We have already seen the use of pipe which is used to communicate between parent and child process. But If the processes are not related we can still communicate by using the named pipe or fifo. Pipes and Fifo are nothing but the redirection of the data streams We can create a named pipe of Fifo using the command mknod or mkfifo.

$ mkfifo /tmp/test

This command will create a fifo file in /tmp directory.

We can see the file by using command

$ ls -la /tmp/test prw-r--r-- 1 sherill hadmin 0 Dec 10 16:24 /tmp/test|

The initial p character shows its a fifo file.

Now open two terminals on one type

$ read line < /tmp/test This read will blocked until it read something from fifo file.

In another terminal enter command

$ echo "Fifo Example" > /temp/test

This command will enter text in the fifo, and the read command on previous terminal will be completed. Now if you type echo $line on the previous terminal you will see that the data has successfully being read.

If you run the same command again the read will wait for the data, as the Fifo is empty due to last read.

We can use a timeout with read to stop hanging infinitely on the input.

for example

exec 7<> /tmp/testwhile true;do

   read -t0 -u7 line
   if [ $? -eq 0 ];then
       echo "$line is read"
       # Do something
   else
       echo " No Data. Sleep"
       # Do something else
   fi

done

Use of fifo

We have seen how we can use the redirection to redirect the input or output to or from a program using pipe. But this works only in one way i.e. either you redirect input from the command or you redirect output to that command. If we need to redirect the input to a program and along with its output to be read by same program then we can do this using fifo. For example here we want to redirect output from netcat to shell and then read the output of this shell back so in netcat. So we created a fifo and redirected the output of netcat command to fifo. Then we are reading this fifio using cat and redirectin it to shell which process the data and then its output is redirected to netcat again.

$ mkfifo /tmp/myfifo $ cat /tmp/myfifo | /bin/sh | nc -l 1356 > /tmp/myfifo

This simple command will produce a netcat server running a shell which can be accesed using command

$ netcat localhost 1356

If you want to access the shell from a remote machine just change the localhost to the IP address of the machine and you get yourself a remote shell.


Indirect Reference

We can easily reference a variable value with $variable syntax, but if we need value of a value .i.e indirect reference. Bash also supports the indirect references with the help of eval command and \$$variable syntax. e.g.

x=10 y=x

echo $(eval echo \$$y)

We can use indirect references to create a look up table.

Lets create a program which will print the frequency of each word.

  1.  !/bin/bash
  2.  !/bin/bash
  3. word_freq

function usage {

   echo "usage: $0"

}

  1. Check argument is given and it is a file

if [ -z "$1" -o ! -f "$1" ];then

   usage
   exit 1

fi

  1. Declare array

declare -a map

  1. Open File

exec 7<> "$1" index=0 while read -a array -u 7 ;do

   count=${#array[@]}
   # Traverse Each Word
   for((i=0;i&-

echo "Total $index words Found" count=$index

  1. Print Frequency

for((i=0;i<$count;i++));do echo -n "${map[$i]}:"

   elem=${map[$i]}
   # Indirect Reference to word's frequency
   echo $(eval "echo \$elem")

done

I ran this script with following input.

$ cat input.txt This is an input to word frequency script This script will occurrence the frequency of each word and report back This script uses bash indirect references to create a Map and uses it to save occurrence

$ ./word_freq input.txt Total 25 words Found This:3 is:1 an:1 input:1 to:3 word:2 infrequence script:3 will:1 occurrence:2 the:1 of:1 each:1 and:2 report:1 back:1 uses:2 bash:1 indirect:1 references:1 create:1 a:1 Map:1 it:1 save:1

This script is just an example of indirect reference and is very simple and will not handle many words for example if any of the variable ecount, map, count etc are in the input then it will not work correctly also it is not able to handle any special characters. But with little effort it can be made to handle all the cases this can be a good learning exercise.


Networking

There are two ways to use networking in your scripts Bash internal socket handling

As we know all devices in linux are files, we can open then using open system call and can do basic input output. This can also be done with devices /dev/cp and /dev/udp.

From the bash manpage:

   /dev/tcp/host/port
   If host is a valid host name or Internet address, and port is an integer port number or service name, bash attempts to open a TCP connection to the corresponding socket.
   /dev/udp/host/port
   If host is a valid host name or Internet address, and port is an integer port number or service name, bash attempts to open a UDP connection to the corresponding socket.


For example to get home page of Google in your script, without using any other tool we can use following commands.

$ exec 7/dev/tcp/mylinuxbook.com/80 $ echo -en "GET / HTTP/1.0\r\nHost: mylinuxbook.com\r\nConnection: close\r\n\r\n">&7 $ cat &-

In the first line we are opening a connection with the server mylinuxbook.com at port 80 with the file descriptor 7, then we sent a Get request for Home page using HTTP protocol. Then are reading the server response using cat command. At last when its done we close the connection using exec command. Using External Tools

Depending on need there are numerous tools available on linux to choose from. One among them is Netcat. Which is also called “Swiss-army knife” of networking which can be used to create TCP as well as UDP connections.

Lets create a simple time server using netcat.

At server (172.31.100.7) enter command

$ while true; do date | nc -u -l 13 ; done

This command will create a udp server listening at port 13. When any machine connects it will return the output of date command. And then exit. While loop is to create the server again.

Now to get time at any machine enter command

$ nc -u 172.31.100.7 13

This command will connect to serer 172.31.100.7 at the port 13 and retrieve print the data received from the server

To know more about it read necat. Apart from necat wget and curl are also great tools to which are used used inside the scripts for handling networking.


Single Instance Programs (.lock File)

.lock files are used to constrained our script to run in single instance mode, specially in the case when multiple instance can corrupt the system state like aptd trying to install a software or updating. If multiple aptd are allowed to run in parallel they may corrupt the database. Usually lock files are present in the /var/lock or /tmp directory.

Lets create a script which allows only single instance to run.

  1.  !/bin/bash
  2. single_instance [-n]
  3. If -n option given Kill

APP_NAME=single_instance DIR=/tmp LOCK_FILE=${DIR}/${APP_NAME}.lock PID_FILE=${DIR}/${APP_NAME}.pid

  1. An Array for temporary files

declare -a temp_files

  1. Add Temporary files in the global array

function add_file() {

   local index=${#temp_file}
   if [ ! -z "$1" -a -f "$1" ];then
       index=$((index+1))
       temp_files[$index]="$1"
   fi

}

  1. Perform the cleanup and exit

function cleanup_and_exit() {

   local count=${#temp_file}
   for((i=0;i<count;i++))    {
       rm -rf ${temp_files[$i]} >/dev/null 2>&1
   }
   exit 0

}

  1. Check if instance of scipt is already running

function check_single_instance() {

   # Check Lock File
   if [ -f ${LOCK_FILE} ];then
       return 1
   fi
   return 0

}

  1. Add Signal Handler for SIGTERM

trap cleanup_and_exit SIGTERM

  1. Check if instance of this script is running

if check_single_instance ;then

   echo "Instance is running"
   if [ ! -z "$1" -a $1 = "-n" ];then
       # Kill Current Process
       read pid <<<$(cat ${PID_FILE})            if [ ! -z "$pid" ];then
           # Send SIGTERM
           kill $pid
           while kill -0 $pid;do
           echo "Waiting for $pid to exit"
           done
   fi
   else
       exit 0
   fi

fi

  1. Create Lock File

touch ${LOCK_FILE}

  1. Create PID File

echo $$ > ${PID_FILE}

  1. Add files for Clean Up

add_file "${LOCK_FILE}" add_file "${PID_FILE}"

  1. Add Your Script Functionality here

while true;

   echo "This is an example of single Instance Script"
   sleep 1;

done

In the next part of this tutorial series we will use these techniques and create some working programs.


REFERENCES

Advanced Bash Scripting

Bash TCP IP

Getopts Share this:

   LinkedIn
   Reddit
   StumbleUpon
   Digg
   Tumblr
   Pinterest

Tags: bash scripting, bash shell scripting, Linux scripting, Linux shell, shell scripting

Rajeev Get Updates

Subscribe to our e-mail newsletter to receive updates. Share This Post Related Articles

   Bash shell scripting – Part I
   Linux Shell environment

Leave a Reply

Name (Required)

Mail (will not be published) (Required)

Website

Complete this CAPTCHA 'single_linebreak' style='background-color:lightgray'>¶ five − 1

Notify me of follow-up comments by email.

Notify me of new posts by email. ©2013 MyLinuxBook- All rights reserved Subscribe to our new posts and monthly Newsletter

To subscribe to our new posts and monthly newsletter simply add your email below. A confirmation email will be sent to you!

© 2013 MyLinuxBook. All rights reserved. Site Admin · Entries RSS · Comments RSS Powered by WordPress · Designed by Theme Junkie