public:books:linux_shell_scripting_cookbook:chapter_1

  • We cannot echo exclamation mark (!) in double quotes. But these will work:
    echo Hello world !
    echo 'Hello world !'
    echo "Hello world \!"
  • We can use printf instead of echo
  • No new line by default with printf, so we have to add it when needed:
    printf  "%-5s %-10s %-4s\n" No Name  Mark 
    printf  "%-5s %-10s %-4.2f\n" 1 Sarath 80.3456 
    printf  "%-5s %-10s %-4.2f\n" 2 James 90.9989 
    printf  "%-5s %-10s %-4.2f\n" 3 Jeff 77.564
  • In '%-5s' above, '-' is used to specify the left alignment, otherwise the text is right aligned.
  • echo can be used with escape sequences when the flag -e is provided (with double-quoted strings):
    echo -e "1\t2\t3"
  • We can change the output color with escaped codes: for instance: reset = 0, black = 30, red = 31, green = 32, yellow = 33, blue = 34, magenta = 35, cyan = 36, and white = 37:
    echo -e "\e[1;31m This is red text \e[0m"
  • For colored background use: reset = 0, black = 40, red = 41, green = 42, yellow = 43, blue = 44, magenta = 45, cyan = 46, and white=47:
    echo -e "\e[1;42m Green Background \e[0m"
  • Get process ID with pgrep, for instance:
    pgrep gedit
  • List the environment variables of a process with:
    cat /proc/$PID/environ
Previous commands doesn't seem to work in cygwin.
* We can use **tr** to substitute characters:<code>echo "Coucou manu." | tr 'o' '8'</code>
  • To find length of a string:
    length=${#var}
  • To Identifying the current shell:
    echo $SHELL
  • To check for super user:
    If [ $UID -ne 0 ]; then
        echo Non root user. Please run as root.
    else
        echo Root user
    fi
  • To modify the Bash prompt string:
    PS1="PROMPT>"
  • For the PS1 var there are also certain special characters that expand to system parameters. For example, \u expands to username, \h expands to hostname, and \w expands to the current working directory.
  • We can add in .bashrc the method:
    prepend() { [ -d "$2" ] && eval $1=\"$2':'\$$1\" && export $1; }
  • Or, to fix the issue of empty variable when predending:
    prepend() { [ -d "$2" ] && eval $1=\"$2\$\{$1:+':'\$$1\}\" && export $1 ; }
  • Note: ${parameter:+expression} will expand to expression if parameter is set and not null.
  • And then call:
    prepend PATH /opt/myapp/bin
    prepend LD_LIBRARY_PATH /opt/myapp/lib
  • We mainly use let, ( ( ) ), [], expr and bc.
  • Using let for basic operations:
    no1=4;
    no2=5;
    let result=no1+no2
    echo $result
    
    # incrementation:
    let no1++  
    
    # decrementation:
    let no1--  
    
    # shorthands:
    let no+=6
    let no-=6
  • Using [] operator as alternate method:
    result=$[ no1 + no2 ]
    
    # using the $ prefix inside [] is legal:
    result=$[ $no1 + 5 ]
  • Using ( () ) or expr:
    result=$(( no1 + 50 ))
    result=`expr 3 + 4`
    result=$(expr $no1 + 5)
  • For floating point operations we can use bc:
    echo "4 * 0.56" | bc
    
    no=54; 
    result=`echo "$no * 1.5" | bc`
    echo $result
    
    # passing additional parameters to bs with prefixes to the operation
    # and semicolon delimiters:
    
    # Decimal places scale with bc:
    # Note that the default scale is 0 and bc is thus outputting integers.
    echo "scale=2;3/8" | bc
    
    # Base conversion with bc:
    no=100
    echo "obase=2;$no" | bc
    
    no=1100100
    echo "obase=10;ibase=2;$no" | bc
    
    # Calculating square roots and pows:
    echo "scale=3;sqrt(10)" | bc
    echo "10^10" | bc
  • Reserved file descriptors are:
    • 0: stdin
    • 1: stdout
    • 2: stderr
  • Saving to a file is done with:
    echo "This is a sample text 1" > temp.txt
  • To append to a file we use:
    echo "This is a sample text 1" >> temp.txt
  • To view file content:
    cat temp.txt
  • To redirect stderr exclusively to a file and stdout to another file:
    cmd 2>stderr.txt 1>stdout.txt
  • To redirect stderr and stdout to a single file by converting stderr to stdout:
    cmd 2>&1 >output.txt
  • We can also redirect to /dev/null to discard an output stream:
    cmd 2>/dev/null
  • We can use the tee command to make a copy of the output stream instead of redirecting:
    # Prototype: command | tee FILE1 FILE2
    
    cat a* | tee out.txt | cat -n
    
    # To append to a file we use the flag -a:
    cat a* | tee -a out.txt | cat -n
  • We can use stdin as a command argument. It can be done by using - as the filename argument for the command:
    cmd1 | cmd2 | cmd -
  • Redirection from a file to a command:
    cmd < file
  • Redirection froma text block within script:
    cat<<EOF>log.txt
    LOG FILE HEADER
    This is a test log file
    Function: System statistics
    EOF
  • To create custom file descriptors:
    exec 3<input.txt # open for reading with descriptor number 3
    
    echo this is a test line > input.txt
    exec 3<input.txt
    
    # Use the descriptor:
    cat<&3
    
    # To use the descriptor again we must reassign it with exec.
    
    # For write descriptor with truncate mode:
    exec 4>output.txt
    echo newline >&4
    cat output.txt
    
    # For write descriptor with append mode:
    exec 5>>output.txt
    echo newline >&5
    cat output.txt
  • For associative arrays we need bash version 4 or higher.
  • Array definitions:
    # Values are stored in consecutive locations starting
    # from index 0:
    array_var=(1 2 3 4 5 6)
    
    # Definition with set of index-value pairs:
    array_var[0]="test1"
    array_var[1]="test2"
    array_var[2]="test3"
    array_var[3]="test4"
  • To print array content:
    # Print content at given index:
    echo ${array_var[0]}
    
    index=5
    echo ${array_var[$index]}
    
    # Print all the values:
    echo ${array_var[*]}
    
    # or:
    echo ${array_var[@]}
  • To get array legnth:
    echo ${#array_var[*]}
  • Using associative arrays:
    declare -A ass_array
    
    # Then we add elements with:
    ass_array=([index1]=val1 [index2]=val2)
    
    # Or:
    ass_array[index1]=val1
    ass_array'index2]=val2
    
    # For instance:
    declare -A fruits_value
    fruits_value=([apple]='100dollars' [orange]='150 dollars')
    echo "Apple costs ${fruits_value[apple]}"
    
    # To list array indexes:
    echo ${!array_var[*]}
    
    # Or:
    echo ${!array_var[@]}
  • Create an alias with:
    alias new_command='command sequence'
    # For instance:
    alias install='sudo apt-get install'
  • To keep permanent aliases, we have to add them in .bashrc:
    echo 'alias cmd="command seq"' >> ~/.bashrc
  • To Remove alias:
    unalias example
    # or:
    alias example=
  • We can escape aliases with exacuting a command with:
    \command
  • Using tput and stty:
    # Get num columns and rows:
    tput cols
    tput lines
    
    # get current terminal name:
    tput longname # not working on cygwin
    
    # Move cursor to position 10,10:
    tput cup 10 10
    
    # set background color n=0 to 7:
    tput setb n
    
    # set foreground color n=0 to 7:
    tput setf n
    
    # show bold text:
    tput bold
    
    # start and end underlining:
    tput smul
    tput rmul
    
    # delete from cursor to end of line:
    tput ed
  • To hide a password we use stty:
    echo -e "Enter password: "
    stty -echo
    read password
    stty echo
    echo
    echo Password read.
  • To Read date:
    date
    
    # to print the epoch (num secs since 1970-01-01 00:00:00 UTC):
    date +%s
    
    # find out the epoch of a given date:
    date --date "Thu Nov 18 08:07:21 IST 2010" +%s
    
    # Retrieve the week day of a date:
    date --date "Jan 20 2001" +%A
    
    # Check time taken by a command:
    start=$(date +%s)
    commands;
    statements;
    end=$(date +%s)
    difference=$(( end - start))
    echo Time taken to execute commands is $difference seconds.
  • We can delay execution in script with the sleep command:
    echo -n Count:
    tput sc # store cursor position
    count=0;
    while true;
    do
        if [ $count -lt 40 ];
        then
            let count++;
            sleep 1; # sleep a given number of seconds
            tput rc # restore cursor position.
            tput ed # clear until end of line.
            echo -n $count;
        else exit 0;
        fi
    done
  • Debug a complete script file:
    bash -x scripts.sh
  • Debug only a portion of the script:
    for i in {1..6};
    do
        set -x
        echo $i
        set +x
    done
    echo "Script executed"
  • We can provide our own formatting for instance with a _DEBUG environment variable:
    function DEBUG()
    {
        [ "$_DEBUG" == "on" ] && $@ || :  # in bash ":" tells the script to do nothing.
    }
    
    for i in {1..10}
    do
        DEBUG echo $i
    done
    
    # Then we would run:
    _DEBUG=on ./script.sh
  • Functions can be defined with:
    function fname()
    {
        statements;
    }
    
    # Or alternately:
    
    fname()
    {
        statements;
    }
  • In a function “$@" expands as "$1” “$2" "$3” and so on
  • In a function “$*" expands as "$1c$2c$3”, where c is the first character of IFS.
  • Functions in bash support recursion:
    F() 
    { 
      echo $1; F hello; sleep 1; 
    }
  • Functions can be exported:
    export -f fname
  • Reading the return value (status) of a command:
    cmd; 
    echo $?;
    
    # For instance:
    CMD="command" #Substitute with command for which you need to test the 
    exit status
    $CMD
    if [ $? -eq 0 ];
    then
        echo "$CMD executed successfully"
    else
        echo "$CMD terminated unsuccessfully"
    fi
  • We can read the output of a sequence with a subshell method:
    cmd_output=$(COMMANDS)
    
    # For instance:
    cmd_output=$(ls | cat -n)
    echo $cmd_output
  • Or with a back quotes:
    cmd_output=`COMMANDS`
    
    # For instance:
    cmd_output=`ls | cat -n`
    echo $cmd_output
  • We can spwan a separate process with ( ):
    pwd; # current working directory
    (cd /bin; ls);
    pwd; # Still the same current working directory
  • Subshell should be quoted to preserve the newline character:
    out="$(cat tex.txt)"
    echo $out
  • Read n chars:
    read -n number_of_chars variable_name
    
    # For instance:
    read -n 2 var
    echo $var
    
    
    # Read a password:
    read -s var
    
    # Display a message before reading:
    read -p "Enter input:" var
    
    # Read after timeout:
    read -t timeout var  # timeout is a number of seconds.
    
    # Read until delimiter:
    read -d delim_char var
    
    # For instance:
    read -d ":" var
  • We can define the repeat method as:
    repeat()
    {
      while true
      do
        $@ && return
      done
    }
  • We can optimize by adding a delay and avoiding the spawn of the “true” binary with:
    repeat() { while :; do $@ && return; sleep 30; done }
  • Update IFS to read data:
    data="name,sex,rollno,location"
    # To read each of the item in a variable, we can use IFS.
    oldIFS=$IFS
    IFS=, now,
    for item in $data;
    do
        echo Item: $item
    done
    IFS=$oldIFS
  • Receive some indexed values on a line with IFS:
    line="root:x:0:0:root:/root:/bin/bash" 
    oldIFS=$IFS;
    IFS=":"
    count=0
    for item in $line;
    do
         [ $count -eq 0 ]  && user=$item;
         [ $count -eq 6 ]  && shell=$item;
        let count++
    done;
    IFS=$oldIFS
    echo $user\'s shell is $shell;
  • We can generate sequences with:
    echo {1..50}
    echo {a..z}
    echo {A..Z}
    echo {a..h}
    
    # for instance:
    for i in {a..z}; 
    do 
      echo $i;
    done;
  • For loop can also take the format of for in C:
    for((i=0;i<10;i++))
    {
        commands; # Use $i
    }
  • While loop format:
    while condition
    do
        commands;
    done
  • until format:
    x=0;
    until [ $x -eq 9 ]; # [ $x -eq 9 ] is the condition
    do
        let x++; echo $x;
    done
  • IF syntax:
    if condition;
    then
        commands;
    fi
  • else if syntax:
    if condition; 
    then
        commands;
    else if condition; then
        commands;
    else
        commands;
    fi
  • To make if conditions shorter we can use logical operators:
    [ condition ] && action; # action executes if the condition is true
    [ condition ] || action; # action executes if the condition is false
  • Performing mathematical conditions is done with:
    [ $var -eq 0 ]  # It returns true when $var equal to 0.
    [ $var -ne 0 ] # It returns true when $var is not equal to 0
  • Available mathematical operators for conditions are:
    • -gt : greater than
    • -lt : less than
    • -ge : greater or equal
    • -le : less than or equal
  • Combining multiple test conditions is done with:
    [ $var1 -ne 0 -a $var2 -gt 2 ]  # using AND -a
    [ $var1 -ne 0 -o var2 -gt 2 ] # OR -o
  • Filesystem related tests:
    [ -f $var ] # This returns true if the given variable holds a regular file path or filename
    
    [ -x $var ] # This returns true if the given variable holds a file path or filename that is executable
    
    [ -d $var ] # This returns true if the given variable holds a directory path or directory name
    
    [ -e $var ] # This returns true if the given variable holds an existing file
    
    [ -c $var ] # This returns true if the given variable holds the path of a character device file
    
    [ -b $var ] # This returns true if the given variable holds the path of a block device file
    
    [ -w $var ] # This returns true if the given variable holds the path of a file that is writable
    
    [ -r $var ] # This returns true if the given variable holds the path of a file that is readable
    
    [ -L $var ] # This returns true if the given variable holds the path of a symlink
  • String comparaisons:
    [[ $str1 = $str2 ]] # This returns true when str1 equals str2
    # that is, the text contents of str1 and str2 are the same.
    
    [[ $str1 == $str2 ]] # It is an alternative method for string equality check
    
    # We can check whether two strings are not the same as follows:
    
    [[ $str1 != $str2 ]] # This returns true when str1 and str2 mismatch 
    
    # We can find out the alphabetically smaller or larger string as follows:
    
    [[ $str1 > $str2 ]] # This returns true when str1 is alphabetically greater than str2
    
    [[ $str1 < $str2 ]] # This returns true when str1 is alphabetically lesser than str2
    
    [[ -z $str1 ]] # This returns true if str1 holds an empty string
    
    [[ -n $str1 ]] # This returns true if str1 holds a nonempty string
  • It is sometimes easier to combine multiple conditions using logical operators:
    if [[ -n $str1 ]] && [[ -z $str2 ]] ;
    then
        commands;
    fi
  • We can use the test command isntead of the [] operator:
    if  [ $var -eq 0 ]; then echo "True"; fi
    # can be written as:
    if  test $var -eq 0 ; then echo "True"; fi
  • public/books/linux_shell_scripting_cookbook/chapter_1.txt
  • Last modified: 2020/07/10 12:11
  • by 127.0.0.1