Bash – Integer Arithmetic Operations

Background

Wanted to get down sample tooling for performing arithmetic options in Bash.

Code


#!/bin/bash

function compareNumbers()
{

	local _message=""

	_number1=($1)
	_number2=($2)

	if [ $_number1 -lt $_number2 ]
	then
	  _message="The first number ( $_number1 ) is less than the second number ( $_number2 ) "

	elif [ $_number1 -eq $_number2 ]

	then
	  _message="The first number ( $_number1 ) is equal to the second number ( $_number2 ) "

	elif [ $_number1 -gt $_number2 ]

	then
	  _message="The first number ( $_number1 ) is greater than the second number ( $_number2 ) "

	else

	  _message="The first number ( $_number1 ) and second number ( $_number2 ) are misunderstood!"

	fi

	echo $_message

}

function calc()
{

	local _message=""
    local _moduleName=`basename "$0"`

	_number1=($1)
    _operator=($2)

	_number2=($3)

    local _result=0
    local _operatorAsString=""

	_operatorAsString="$_operator"

    case "$_operator" in

           +)
               _result=$(expr $_number1 + $_number2);
               ;;                       

           -)
               _result=$(expr $_number1 - $_number2);

               ;;                       

		   /)
               _result=$(expr $_number1 / $_number2);
               ;;                       

		   %)
               _result=$(expr $_number1 % $_number2);
               ;;  

		   '^' | '**')
               let "_result=$_number1**$_number2"
               ;;                  

		   '*'|'mult'|'multiply'|$_moduleName)
               _result=$(expr $_number1 \* $_number2);
               _operatorAsString="mult";
               ;;     

    esac

    _message="$_number1 $_operatorAsString $_number2 = $_result"

	echo $_message

}

declare -i _number1
declare -i _number2

declare _number1
declare _number2

#---------------------------------------------------------#
#Compare                                                  #
#---------------------------------------------------------#
_number1=10
_number2=15

echo '********************************************************'

_mesg=$(compareNumbers $_number1 $_number2 )

echo $_mesg

_number1=15
_number2=15

_mesg=$(compareNumbers $_number1 $_number2)

echo $_mesg

_number1=18
_number2=15

_mesg=$(compareNumbers $_number1 $_number2)

echo $_mesg

_number1=apple
_number2=15

_mesg=$(compareNumbers $_number1 $_number2)

echo $_mesg

echo '********************************************************'
echo
echo

#---------------------------------------------------------#
#Calc                                                     #
#---------------------------------------------------------#

# calc - addition

echo '*****************************************************'

_number1=5
_number2=3
_operator='+'

_mesg=$(calc $_number1 $_operator $_number2  )

echo $_mesg

# calc - subtract
_number1=105
_number2=90
_operator='-'

_mesg=$(calc $_number1 $_operator $_number2  )

echo $_mesg

# calc - divide
_number1=90
_number2=10
_operator='/'

_mesg=$(calc $_number1 $_operator $_number2  )

echo $_mesg

# calc - modulo
_number1=42
_number2=8
_operator='%'

_mesg=$(calc $_number1 $_operator $_number2  )

echo $_mesg

# calc - multiply
_number1=7
_number2=8
_operator='*'

_mesg=$(calc $_number1 $_operator $_number2  )

echo $_mesg

# calc - exponent
_number1=2
_number2=3
_operator='^'

_mesg=$(calc $_number1 $_operator $_number2  )

echo $_mesg

echo '********************************************************'

#---------------------------------------------------------

Takeaway

  1. Arithmetic
    • Multiplication
      • * ( Symbol)
        • Passing the multiple symbol (*) is not so easy
        • I think the reason is that it is considered a special character

Capturing Console Output

Background

I was looking through a Bash Script and I was particularly focused on how logging is being accomplished.

If not done fully well, each step will have two steps.  One will write to the screen ( stdout) and the other to a log file.

tee

I am familiar with tee, but my awareness came about through MS Windows.

It basically allows one to capture stdout output to a file, as well.

Linux /Bash

Outline

In our exercise, we will have two applications.

One a client and the other a controller.

The client will do the work and the controller will co-ordinate and ensure that the client’s work is captured.

Steps

  • Client
    • Do very little
      • Simple commands such as
        • hostname
        • date
        • uptime
  • Controller
    • Also do so little
      • Generate Log file names
        • Base file name
        • Timestamp file name
      • Invoke client and capture into base file name
      • Copy base file unto TS file

 

App – Client


#!/bin/bash

echo -n  "Host :-  "

uname -n

echo "Date :- $(date)"

echo "uptime"

uptime

App – Manager


#!/bin/bash

function getLogFileName()
{

  local _baseFolder=${1}
  local _addTS=${2}
  local _filename=""
  local _moduleName=""
  local _TSFormat="%Y%m%d_%H%M%S"
  local _TSAsString=""

  _moduleName=`basename "$0"`

  _TSAsString=$(date +$_TSFormat)

  _filename="${_baseFolder}/${_moduleName}"

  if [ $_addTS -eq 1 ]
  then

    _filename="${_filename}_${_TSAsString}"

  fi

  _filename="${_filename}.log"

  echo $_filename
}

#Get Log File Name ( base )
_filenameLogBase=$(getLogFileName '/tmp' '0' )

#Get Log File Name ( TS )
_filenameLogTS=$(getLogFileName '/tmp' '1' )

#As part of debug display Log file name
echo "Log file name is $_filenameLogTS"

# indicate client app
client="/tmp/stdout.capture.client.sh"

#execute app and log output to _filename
(exec "$client") | tee $_filenameLogBase

#copy base log file to log file TS
cp $_filenameLogBase $_filenameLogTS

 

MS Windows

Here are good MS Windows version of tee

  1. Google Code Archive
    • wintee
      • Links

Linux – Bash – Arrays

Background

Wanted to provide a quick sample code on using Arrays via Bash on a Linux System

Outline

  1. Declare Array
    • To create an empty array please use ()
      • Syntax
        • variable=()
      •  Sample
        • _register=()
  2. Add new entry to array
    • Syntax
      • array+=(variable)
    •  Sample
      • _register+=($1)
  3. Get Number of elements in array
    • Syntax
      • variable=${#array[@]}
    •  Sample
      • _numberofElements=${#_register[@]}
  4. Reference Array Element
    • Syntax
      • variable=${array[id]}
      • 0 based element id
    •  Sample
      • _number=${_register[$i]}

 

Code


#!/bin/bash

#declare array
_register=()

#add item to array
function bagit() {

    #Append new item
    _register+=($1)

}

#list all array elements
function listNumbers() {

    local _iNumberofElements=${#_register[@]}

    local i=0
    local iOffset=0

    while [ $i -lt $_iNumberofElements ]
    do

        _iOffset=$((i+1))

        _numbers=$((${_register[$i]}))

        echo "$_iOffset) $_numbers"

        i=$[$i+1]

    done

}

#total up array elements
function totalIT() {

    local _iNumberofElements=${#_register[@]}

    local i="0"

    local _total=0

    while [ $i -lt $_iNumberofElements ]
    do

        _total=$(( $_total + ${_register[$i]}))

        i=$[$i+1]

    done

    echo $_total

}

_iNumberofElements=0

#Add the follow elements
bagit 1

bagit 2

bagit 3

bagit 4

bagit 15

bagit 25

#Get number of elements
_iNumberofElements=${#_register[@]}

echo "Number of Elements $_iNumberofElements"

echo "All Elements at same time ${_register[@]}"

#invoke list elements
listNumbers

#add up  all numbers
_summed="$(totalIT)"

echo ""
echo ""
echo "Numbers summed is $_summed"

Linux / Bash – Time Taken

Background

Need to determine how long a bash script task is taken.

Outline

  1. $SECONDS
    • Use $SECONDS
      • Issue $SECONDS and capture current time into variable
      • Perform task
      • Issue $SECONDS and capture time into second variable
      • Perform arithmetic
        • Subtract first variable from second variable
  2. Date Arithmetic
    • Capture date as epoch
      • $dtStarted = $(date +%s)
    • Perform Task
    • Capture new date as epoch
      • $dtCompleted = $(date +%s)
    • Perform arithmetic
      • Subtract first variable from second variable

Used $SECONDS

Code


#!/bin/bash

echo "Started at $(date)"

_dtStarted=$SECONDS

sleep 10

_dtCompleted=$SECONDS

_durationInSeconds=$((_dtCompleted - _dtStarted))

echo "Completed at $(date)"

echo "Time taken is $_durationInSeconds second(s)"

Output

$seconds.20181031.0855PM

Code


#!/bin/bash

_dtFormat="%Y-%m-%d %H:%M:%S %p"

_dtStarted=$(date +%s)
_dtStartedAsString=$(date +"$_dtFormat")

echo "Started ( as TS) $(date)"

echo "Started ( as String) :- $_dtStartedAsString"

echo ""
echo ""

sleep 10

_dtCompleted=$(date +%s)

_dtCompletedAsString=$(date +"$_dtFormat")

_durationInSeconds=$((_dtCompleted - _dtStarted))

echo "Completed ( as TS) $(date)"

echo "Completed (as String ):- $_dtCompletedAsString " 

echo ""
echo ""

echo "Time taken is $_durationInSeconds second(s)"

Output

timeDifferential.20181031.0851PM

 

Dedicated

Dedicated to learnshell.org.

Link

learnshell.org.20181031.0909PM

References

  1. Linux Commands
    • SECONDS
      • oreilly

Linux – printf – Proper Usage

Background

I ran into problems with a little bash code that I was writing.

I was trying to use the printf code format command.

 

Code

Here is the original code.

Original Code


iNumberofProducts=10
strProductName="Apple"
dCostPerProduct=1.25

dProductSubTotal=`echo "$iNumberofProducts*$dCostPerProduct"|bc`

FORMAT_PRODUCT_DETAIL="%d %s at  %.2f each for a sub-total of %.2f \n"

printf $FORMAT_PRODUCT_DETAIL  $iNumberofProducts $strProductName $dCostPerProduct $dProductSubTotal

 

Output

Textual


printf.sh: line 11: printf: %s: invalid number
printf.sh: line 11: printf: at: invalid number
printf.sh: line 11: printf: %.2f: invalid number
printf.sh: line 11: printf: each: invalid number
printf.sh: line 11: printf: for: invalid number
printf.sh: line 11: printf: a: invalid number
printf.sh: line 11: printf: sub-total: invalid number
printf.sh: line 11: printf: of: invalid number
printf.sh: line 11: printf: %.2f: invalid number
printf.sh: line 11: printf: \n: invalid number
printf.sh: line 11: printf: Apple: invalid number
printf.sh: line 11: printf: 1.25: invalid number
printf.sh: line 11: printf: 12.50: invalid number


 

Revised Code

Code


iNumberofProducts=10
strProductName="Apple"
dCostPerProduct=1.25

dProductSubTotal=`echo "$iNumberofProducts*$dCostPerProduct"|bc`

FORMAT_PRODUCT_DETAIL="%d %s at %.2f each for a sub-total of %.2f \n"

printf "$FORMAT_PRODUCT_DETAIL" $iNumberofProducts $strProductName $dCostPerProduct $dProductSubTotal


Code

The solution is pretty simple and it is to quote the format string.

Simply replace printf $FORMAT_PRODUCT_DETAIL with printf $FORMAT_PRODUCT_DETAIL .

 

 

Linux – Listing Files Based on Date

 

Background

I have a need to list files on a Linux Host sorted by date.

 

ls Command

Earliest First

Command


ls -ltr -G -g | grep -v '^total' | head -5

 

Output

 

Latest First

Command


ls -lt -G -g | grep -v '^total' | head -5

Output

 

ls Command In a ditch

Unfortunately, when there are numerous files in the target folder the ls command and other file utilities can get in a ditch.

Argument List Too Long

Command


ls *.LOG

Output

Image

Text

bash: /bin/ls: Argument list too long

 

Find Command

Earliest First

Command


find . -name '*.LOG' | sort | head -n 5

Output

Command


find . -name '*.LOG' | sort | tail -n 5

Output

 

Script

Let us get a bit more complicated.

And, we will do do by picking up individual files, process them, and exit after N Number of files have been processed.


# get current folder
echo "Current working folder is $PWD"


#echo list all files in current log
#find . -name "*.LOG" | xargs -i echo " {} "


declare -i iFileID
declare -i iFileIDMax
declare FORMAT_FILE_PROCESSING

iFileID=1
iFileIDMax=1000
iFileIDMax=50
FORMAT_FILE_PROCESSING="%d  file %s \n"

#echo $FORMAT_FILE_PROCESSING

#list earliest top N LOG files
find . -name "*.LOG" 2> /dev/null  | sort | head -n $iFileIDMax  | while  IFS="" read name;
do



     printf "$FORMAT_FILE_PROCESSING" $iFileID  $name

     iFileID=$((iFileID+1));

     if [ $iFileID  -gt  $iFileIDMax ]
      then


        break;

     fi


done



 

 

Dedicated

Dedicated to ….

The Electronic Toolbox
Argument list too long when copying/deleting/moving files on Linux
Link

Summary

When needing to list files by date, one can use the ls command,

In cases where there are numerous files, the type of filtering one can do is hampered by the fact that the ls command tries to accept all the files as a batch.

A good workaround is the find command.

 

Next Up

The error that I really need to fix was the one from running “gzip S066*

Image

Textual

bash: /usr/bin/gzip: Argument list too long

 

I know it has something to do with 666

 

Reference

  1. Error – Argument list too long …
    • The Electronic Toolbox
      • Argument list too long
        Link
  2. ls command
    • TheGeekStuff.com
      • Linux ls command examples
        Linux
    •  linuxcommand.org
  3. Find Command
    • Unix.com
      • While loop, input from find command
        Link
    • StackOverflow.com
      • Display modified datetime with Find Command
        Link
    • unix.stackexchange.com
      • Only find first few matched files using find
        Link
    • Superuser.com
      • How can I find the oldest file in a directory tree
        Link
  4. Loop Control
    • The Linux Documentation Project ( tldp.org )
    • LinuxCommand.org
      • Flow Control – Part 3
        Link
  5. If Command
    • TheGeekStuff.com
      • Bash If Statement Examples ( If then fi, If then else fi, If elif else fi, Nested if )
        Link
  6. Math
    • K-State Polythecnic
      • Math in Shell Script
        Link
  7.  printf
    • Linuxconfig.org
      • Bash printf basic commands
        Link
    • Bash Hackers wiki
      • The printf command
        Link
    • Unix.StackExchange.org
      • Printf formatting with variable format – what does this var reference?
        Link
  8. variables
    • Ryan

 

Change Prompt in Bash

Background

For us that like to journal and keep notes of every command we issue and capture screens, having long prompts get in the way a bit.

Especially, when you get an incident and pick up that phone and  it says one of the DB/2 hosts is running out of Log space again.

And, the best you can do is say that if it is only at 90% it can likely suffer till the morning.

 

Current Prompt

To get the current prompt issue “echo $PS1

Image

Text

\u@\h:\w>

Revised Prompt

Let us rid ourselves of all the clutter by dropping all three; username, machine name and the current working directory are all out of here.

SESSION


export PS1='>'

 

Permanently

To make the change permanently, please edit ~/profile in your editor of choice and add same line.