Top

A Linux User Reference

Search tips
  • search ignores words that are less than 4 characters in length
  • searches are case insensitve
  • if a search does not return anything try it in Boolean mode then Query expansion mode by checking the appropriate radio button e.g. searching for 'cron' in just the Administration category returns nothing - presumably because the 50% threshold is reached. Boolean mode ignores this threshold so a search for 'cron' returns several hits
  • in Boolean mode preceding a word with a '+' means the result must include that word, a '-' means it must not
  • in Boolean mode '+crontab -anacron' means match articles about crontab that DO NOT mention anacron
  • to match a phrase e.g. 'manage system' check the Boolean mode radio button and enclose the phrase in quotes "some phrase ..."
  • in Query expansion mode the search context is expanded beyond the keywords you entered - relevancy of hits may well be degraded

THE SHELL

Arrays

  • Bash arrays
    • Bash provides one-dimensional array variables, you can simulate multi-dimensional arrays.
    • Any variable may be used as an array; the declare builtin will explicitly declare an array.
    • There is no maximum limit on the size of an array, nor any requirement that members be indexed or assigned contiguously.
    • Arrays are indexed using integers and are zero-based.
  • Declaring an array

    Examples

    $ declare -a array1                 (Initialise an empty array)
    $ declare -a array2[]               (As above)
    
    $ array3[0]="str1"                  (Initialise and populate the first element)
    $ array3=("str1")                   (As above)
    
    $ array4=("a" "b" c)                (Initialise an array and populate)
    $ array4=([0]="a" [1]="b" [2]=c)    (As above but rarely used)
    
    $ echo ${#array4[@]}                (Display the length of the array)
    3
    
    $ echo ${#array3[0]}                (Display the length of the element at index=0)
    4
    
    $ echo ${!array4[@]}                (Display list of array indices (keys))
    0 1 2
    
    $ array1=(1 2 3 4 5 6)
    $ unset array1[1]
    $ unset array1[4]                   (Delete/remove elements at index=1 and 4)
    $ echo ${array1[@]}                 (Display elements as a 'word' separated string)
    1 3 4 6
    
    $ echo ${array1[@]:1:1}             (May appear that deleted indexes are reassigned
    3                                    i.e. the arrays indices are reordered.
                                         however this is not the case. Index 1 still
    $ echo ${array1[1]}                  exists only the value of it's element has
                                         been deleted.)
    $ echo ${array1[2]}
    3
    
    $ unset array1                      (Delete the array, same as $ unset array1[@])
    $ echo ${#array1[@]}
    0
    
  • Copying an array

    Examples

    $ array1=(1 2 3)
    $ array2=("${array1[@]}")                   (Copy)
    
    $ for (( i=0; i<${#array2[@]}; i++ ))
    > do
    >    echo ${array2[$i]}
    > done                                      (Display copy)
    1
    2
    3
    
  • Array element selection

    Examples

    $ array1=(hello there everyone)
    
    $ echo ${array1[@]:0:1}             (Display one element starting from index=0)
    hello
    
    $ echo ${array1[0]}                 (As above)
    hello
    
    $ echo ${array1[@]:0:2}             (Display two elements starting from index=0)
    hello there
    
    $ echo ${array1[@]:0}               (Display all elements starting from index=0)
    hello there everyone
    
    $ echo ${array1[@]:0:3}             (As above)
    hello there everyone
    
    $ echo ${array1[@]:1:2}             (Display two elements starting from index=1)
    there everyone
    
    $ echo ${array1[0]/he/xx}           (Replace 'he' with 'xx' in first element)
    xxllo
    
    $ echo ${array1[0]##*l}             (Remove longest match from start of first element)
    o
    
    $ echo ${array1[0]#*l}              (Remove shortest match from start of element)
    lo
    
    $ echo ${array1[@]%%*ere}           (Remove longest match from end of each element)
    hello everyone
    
    $ echo ${array1[@]/%*ere/again}     (Replace shortest match from the end with 'again')
    hello again everyone
    
  • Simulate a hash using shell arrays

    See TLDP Advanced bash scripting guide.

    Example simulated hash

    #!/bin/bash
    
    # Reference: TLDP - Advanced bash scripting guide
    
    # Based on the original
    array_1=(first_name="Mark" middle_name="Simon" surname="Smith")
    array_2=(array_hash=${array_1[*]})
    
    hash_it(){
      arr1="array_2[*]"
      local ${!arr1}                        # Restrict visible scope of 'keys'
                                            # to this function and it's children
      arr2="array_hash[*]"
      local ${!arr2}
      echo "key first_name: $first_name"
      echo "key middle_name: $middle_name"
      echo "key surname: $surname"
      echo "key age: $age"
      echo "---------------"
    }
    
    
    hash_it
    array_1[0]=first_name=Tony
    array_1[3]=age=47
    array_2=(array_hash=${array_1[*]})
    hash_it
    exit 0
    

    Running the script

    $ ./hash_test.sh
    key first_name: Mark
    key middle_name: Simon
    key surname: Smith
    key age:
    ---------------
    key first_name: Tony
    key middle_name: Simon
    key surname: Smith
    key age: 47
    ---------------
    
  • Implement an array as a stack

    The script's menu navigation is controlled by a stack implemented via an array.

    #!/bin/bash
    # Script to display some information that is held in a mysql DB
    # mark - 3.8.09
    
    # Uncomment to aid debug
    #set -x
    
    # max num of views in the top-level menu
    max_labels=16  
    
    # stores a result of a sql query                            
    wrk="/tmp/work_file"
    
    # generated sql query statements
    sql="/tmp/query.sql"  
    
    # source the config file so can use it's variables
    source /home/mark/dbview/dbviewer.conf
    
    # simulate a stack, first data item is the name of the first menu to display
    menu_stack=( "menu_1" )
    
    # array into which menu arrays are copied 
    menu_labels=()
    
    # Function to display the main menu
    #----------------------------------
    dyn_menu() {
      # Delete working files, comment out to aid debugging
      [[ -e $sql ]] && rm $sql 2>&1 > /dev/null
      [[ -e $wrk ]] && rm $wrk 2>&1 > /dev/null
    
      clear
      echo -e "\n\t$db_name:\t\t$today\n\n\tAvailable Views:\n\t----------------\n"
    
      # need to change field separator else array elements are 
      # defined using 'white space'.  Store current IFS.
      old_ifs="$IFS"
      IFS=$'\n' 
    
      # get name of menu array to display - from top of stack
      menu_name="${menu_stack[${#menu_stack[@]}-1]}"
    
      # set up variable to copy contents of menu to be displayed
      t1="\${"$menu_name"[@]}"
    
      # copy menu array contents to array menu_labels 
      eval menu_labels=( "$t1" ) 
    
      # determine number of array elements/labels, if greater than
      # max allowed then ignore the rest
      (( ${#menu_labels[@]} >= $max_labels )) && 
         num_labels=$max_labels || 
           num_labels=${#menu_labels[@]}
    
      # Display the main menu, ignore any View menu items which exceed max_labels
      for ((x=0; x<$num_labels; x++))
      do
        echo -e "\t("$((x+1))")\t"${menu_labels[$x]%%:*}
      done
    
      # If this is not the main menu provide a 'previous menu' option
      (( ${#menu_stack[@]} > 1 )) && echo -e "\n\t(p|P)\tPrevious menu\c"
      echo -e "\n\t(q|Q)\tQuit"
      echo -e "\n\n"
    
      IFS="$old_ifs"
      ok="1"
    
      # keep prompting if input is invalid
      while [[ $ok != "0" ]]
      do 
        echo -e "\t\c"
        read -p "Please enter number or letter and press return >" choice
        ###-
        if [[ $choice > 0 && $choice < $(($num_labels + 1)) ]]
        then
          # Find out what to do next - run a view or display a sub menu
          view_params=${menu_labels[$(($choice -1))]#*:}
          ###--
          if [[ $view_params =~ "menu_" ]]
          then
            # A sub menu - add it to the menu stack and display
            menu_to_add=${view_params%%:*}
            menu_stack=( "${menu_stack[@]}" "$menu_to_add" )
            dyn_menu
          else
            # A view - run it using any alternative 'mysql' parameters if they exist
            view=$(eval echo "${view_params%%:*}")
            echo "$view" > $sql
            params=${view_params##*:}
            ###---
            if [[ -z $params ]]
            then
              # Use default 'mysql' call
              mysql -t -u $db_user $db_name < $sql > $wrk
            else
              # Use user defined 'mysql' call
              mysql $params < $sql > $wrk
            fi
            ###---
            display_query
            dyn_menu
          fi
          ###--
        else
          case "$choice" in
                        # 'pop' the current menu from the stack
            "p" | "P")  (( ${#menu_stack[@]} > 1 )) && 
                           unset menu_stack[${#menu_stack[@]}-1]
                           dyn_menu;;
            "q" | "Q")  exit 0;;
            *        )  dyn_menu;;
           esac
        fi
        ###-
      done
      return
    }
    
    # Function to display results
    #----------------------------
    display_query(){
      # number of table rows including header and footer
      lines_in_result=$(wc -l $wrk | cut -d" " -f1)
    
      ###-
      if (( $lines_in_result > $lines_per_page ))
      then
        # Display multi-page results
        # Lines 1-3 of the result file are the table column headers. 
        # These plus the last line will envelope each page of 'pure' data rows
        # Line 4 is the first line of 'pure' data
    
        # number of 'pure' data rows for each page
        data_per_page=$((lines_per_page-5))
        orig_per_page=$data_per_page
    
        # each sequence number represents the first line of data for each page 
        page_numbers=$(seq 4 $data_per_page $((lines_in_result -4)))
    
        # maps the first line of a page with the appropriate line from the results file
        pages=[]
        idx=0
        for page in $page_numbers
        do
          pages[$idx]=$page
          let idx=$idx+1
        done
    
        # index for pages[]
        current_page=0 
        title=${menu_labels[$((choice -1))]%%:*}
        len=${#title}
    
        # store title's underline in variable underln
        printf -v underln '%*s' "$len" 
    
        # Loop to display result pages
        p="0"
        while [[ $p != "1" ]]
        do 
          clear
          echo -e "\n\t$title"
          echo -e "\t${underln// /-}\n"
    
          # display table column headers
          sed -n '1,+2s/^/\t/;/^\t/p' $wrk  
    
           # paginate the 'pure' data rows
          sed -n ${pages[$current_page]}',+'$((data_per_page-1))'s/^/\t/;/^\t/p' $wrk 
    
          ## display last line of the table
          sed -n '$s/^/\t/;/^\t/p' $wrk 
    
          echo -e "\n\n\t\c"
          read -p "Enter: p (previous page), n (next page), b (back to menu) >" page_choice
          case "$page_choice" in
                          # Can do 'previous page' if current page is not the first page
                          # data_per_page is altered whenever the last page is displayed, 
                          # set to it's original value
            "p" | "P")    [[ $current_page > 0 ]] && let current_page=$current_page-1
                          data_per_page=$orig_per_page
                          continue 1;;
                          # Can do 'next page' if current page is not the last page
                          # If next page is the last page then set data_per_page to the 
                          # number of rows left to display
            "n" | "N")    [[ $current_page < $((${#pages[@]}-1)) ]] && 
                             let current_page=$current_page+1
                          [[ $current_page == $((${#pages[@]}-1)) ]] && 
                             let data_per_page=$((lines_in_result - ${pages[$current_page]})) 
                             continue 1;;
            "b" | "B")    p="1";;
            *        )    continue;;
          esac
        done
      else
        # Display single page results
        clear
        title=${menu_labels[$((choice -1))]%%:*}
        echo -e "\n\t$title"
        printf -v underln '%*s' "${#title}"
        echo -e "\t${underln// /-}\n"
        cat $wrk | awk '{ print "\t"$0 }'
        echo -e "\n\n\t\c"
        read -p "Press 'Enter' to return to menu >"
      fi
      ###-
    }
    
    # display menu
    dyn_menu
    exit 0
    

    Script's output