How to optimize the following script - bash

This script runs a query to get a list of dates and runs two other queries for these dates.
Then compares which one is the smaller of the numbers and multiplies it by 2.
Then writes to file and sums them.
Please suggest improvements. Also checks for 0 numbers.
#!/bin/bash
1>output.txt
today=$(date +"%Y%m%d")
FirstOfTheMonth=$(date -d "--$(($(date +%-d)-1)) day" `enter code here`+"%Y%m%d")
echo "XXXX activity report on daily and cumulative monthly `enter code here`basis "
#query that outputs dates to a file
SQL query > list
#for each date I run 2 queries
for i in `cat list`;do
a1=SQL query;
b1=SQL query;
# I compare to find out which one is the smaller number and `enter code here`multiply it by 2
buy=${a1#-}
sell=${b1#-}
echo "XXX report for $yesterday month = $i "
echo "Buy $buy"
echo "Sell $sell"
if [ "$buy" -lt "$sell" ];
then DayNumber=$[buy * 2];
else DayNumber=$[sell * 2];
fi;
#I write all the numbers to a file since I have to sum them
MonthNumber=`awk '{ sum += $1 } END { print sum }' `enter `enter code here`code here`DayNumber$i`
echo "Day Number $DayNumber"
echo "$DayNumber$i $MonthNumber$1 $yesterday" >> DayNumber$i
echo "Day Number since $FirstOfTheMonth $MonthNumber$1"
echo ---------------------------------------------------------------------------------------
done
/usr/bin/mail -s "XXXX report $today" xxx#xxxx.com < `enter code here`output.txt

You are most likely getting down votes, do to the size of the code, lack of clarity on what you want, not showing effort to get that result on your own, and very rudimentary mistakes in code readability that could be fixed with a web search for a tutorial. Google has a shell style guide that you should read.
If you are redirecting stdout to a file, you are probably running autonomously. Which means you should redirect stderr too.
exec &>output.txt
First of the month-- not sure why you don't just do this
FirstOfTheMonth=$(date +%Y%m1)
For Pete's sake, indent! This makes me want to beat scripters. Also, don't use i unless it's a very small loop. Use a variable that means something.
while read -rd' ' month; do
some commands
if [[ $buy -lt $sell ]]; then
do this thing here
fi
done
Bash isn't semi-colon terminated, you don't need one at the end of a line. Use the interan [[ ]] over the externam [ ]. Don't quote a variable (e.g. "$buy") to do a numeric comparison (e.g. -lt). Keep your then on the same line as if-- it servers no other purpose than to make it more readable.

Related

Having difficulty defining conditions to call certain functions and error messages

I'm writing a piece of code which will use data from a file that I've already made in order to work out the average value of the file, the minimum value, maximum value and then finally displaying all values at once.
I'm very new to unix so I'm trying to learn it but I just cant seem to crack where I need to go with my code in order for it to gain functionality.
I've got the basics of the code but I need to find a way to call the functions using the year, which is stored in a directory corresponding to that year which is making me think I'm going to have problems calling from the file as I'm using a sed function to only take line 4 of that file rather than the year.
I also need to figure out how to set error messages and status to the script if they have not stated (Year) (One of the 4 commands), the year doesnt correspond to one available in the tree and the keyword is invalid.
Any help or even pointers towards good material to learn these things would be great.
Here is my current code:
#!/bin/bash
#getalldata() {
#find . -name "ff_*" -exec sed -n '4p' {} \;
#}
#Defining where the population configuration file is which contains all the data
popconfile.txt=$HOME/testarea
#Function to find the average population of all of the files
averagePopulation()
{
total=0
list=$(cat popconfile.txt)
for var in "${list[#]}"
do
total=$((total + var))
done
average=$((total/$(wc -l popconfile.txt)))
echo "$average"
}
#Function to find the maximum population from all the files
maximumPopulation()
{
max=1
for in `cat popconfile.txt`
do
if [[ $1 > "$max" ]]; then
max=$1
echo "$max"
fi
done
}
#Function to find the minimum population from all the files
minimumPopulation()
{
min=1000000
for in `cat popconfile.txt`
do
if [[ $1 < "$min" ]]; then
max=$1
echo "$min"
fi
done
}
#Function to show all of the results in one place
function showAll()
{
echo "$min"
echo "$max"
echo "$average"
}
Thanks!
Assuming your popconfile.txt format is
cat popconfile.txt
150
10
45
1000
34
87
You might be able to simplify your code with :
for i in $(cat popconfile.txt);do
temp[$i]=$i
done
pop=(${temp[*]})
min=${pop[0]}
max=${pop[$((${#pop[*]}-1))]}
for ((j=0;j<${#pop[*]};j++));do
sum=$(($sum+${pop[$j]}))
done
average=$(($sum/${#pop[*]}))
echo "maximum="$max
echo "minimum="$min
echo "average="$average
Be aware though that the average here or in your code is calculated with integer mathematics, so you're loosing all decimals.

bash script to compare number inside the 2 files

I want to compare 2 number from two different file using Bash script. The file is tmp$i and tmp$(($i-1)). I have tried the script below is not working
#!/bin/bash
for i in `seq 1 5`
do
if [ $tmp$i -lt $tmp$(($i-1)) ];then
cat tmp$i >> inf
else
cat tmp$i >> sup
fi
done
Sample data
Tmp1:
0.8856143905954186 0.8186070632371812 0.7624440603372680 0.7153352945456424 0.6762383806114797 0.6405457936981878
Tmp2:
0.5809579333203458 0.5567050091247218 0.5329405222386163 0.5115305043007474 0.4963898045543342 0.4846139486344327
You are not setting $tmp so you end up simply comparing whether i is smaller than i-1 which of course it isn't.
Removing the dollar sign nominally fixes that, but will just compare two strings (for which numeric cardinality isn't well-defined, so in practice, always false), not access the contents of files named like those strings. tmp2 is neither larger nor smaller than tmp1. (Bash can perform lexical comparison, but test ... -lt isn't the tool to do that.)
Try this instead:
if [ $(cat "tmp$i") -lt $(cat "tmp$((i - 1))") ]; then
In response to the observation that you want to do this on decimal numbers, you need a different tool, because Bash only supports integer arithmetic. My approach would be to write a simple Awk script which performs the comparison.
In order to be able to use it as a conditional, it should exit(0) if the condition is true, exit(1) otherwise.
In order to keep the main script readable, I would encapsulate it in a function, like this:
smaller_first_line () {
awk 'NR==1 && FNR==1 { i=$1; next } FNR==1 { exit($1 < i) }' "$1" "$2"
}
if smaller_first_line "tmp$i" "tmp$((i - 1))"; then
:

Adding two decimal variables and assigning values in bash

we have been asked to parse a csv file and perform some operations based upon the data in the csv
I am trying to find the maximum of addition of two numbers which i get from the csv file
that is the last and second last numbers, which are decimals
Following is my code
#!/bin/bash
#this file was created on 09/03/2014
#Author = Shashank Pangam
OLDIFS=$IFS
IFS=","
maxTransport=0
while read year month hydro geo solar wind fuel1 biomassL biomassC totalRenew fuel2 biodieselT biomassT
do
while [ $year -eq 2012 ]
do
currentTransport=$(echo "$biodieselT+$biomassT" | bc)
echo $currentTransport
if (( $(echo "$currentTransport > $maxTransport" | bc -l)));
then
$maxTransport = $currentTransport
echo $maxTransport
fi
done
echo -e "Maximum amount of energy consumed by the Transportation sector for year 2012 : $maxTransport"
done < $1
and the following is my csv file
2012,January,2.614,0.356,0.006,0.021,114.362,14.128,1.308,66.74,196.539,199.536,81.791,
2012,February,2.286,0.333,0.007,0.017,107.388,13.952,1.304,61.277,183.921,186.564,81.545,
2012,March,0.356,0.009,0.02,108.268,15.588,1.404,63.444,188.705,191.318,87.827,11.187,
2012,April,,0.344,0.012,0.019,103.627,14.229,1.381,60.683,179.919,181.993,86.339,11.518,
2012,May,,0.356,0.012,0.01,109.644,13.789,1.473,63.611,188.517,190.913,92.087,12.09,
2012,June,,0.344,0.013,0.013,108.116,13.012,1.434,61.056,183.618,185.65,89.673,12.461,
2012,July,,0.356,0.017,0.008,112.426,14.035,1.403,58.057,185.921,187.61,87.707,10.464,
2012,August,0.356,0.016,0.008,113.64,14.01,1.513,60.011,189.174,190.999,94.592,11.14,
2012,September,1.513,0.344,0.015,0.01,110.84,13.435,1.324,56.047,181.647,183.528,82.814,
2012,October,1.83,0.356,0.012,0.02,111.544,15.597,1.462,57.365,185.969,188.186,91.42,
2012,November,2.022,0.344,0.01,0.014,111.808,15.594,1.326,56.793,185.521,187.911,82.919,
2012,December,1.77,0.356,0.007,0.022,116.416,15.873,1.368,58.741,192.398,194.552,85.526,
2013,January,3.021,0.357,0.007,0.018,114.601,15.309,1.334,57.31,188.553,191.956,83.415,
2013,February,3.285,0.322,0.012,0.023,102.499,13.658,1.246,52.05,169.452,173.094,77.914,
2013,March,0.357,0.016,0.025,111.594,14.538,1.419,59.096,186.646,189.884,88.713,11.938,
2013,April,,0.345,0.018,0.03,103.602,14.446,1.437,59.057,178.542,181.342,89.867,12.184,
2013,May,,0.357,0.02,0.032,108.113,14.452,1.497,62.606,186.668,190.117,93.634,13.166,
2013,June,,0.345,0.021,0.028,109.162,14.597,1.47,61.563,186.792,189.994,91.894,14.501,
2013,July,,0.357,0.018,0.024,119.154,15.018,1.45,62.037,197.659,201.027,90.689,14.523,
2013,August,0.357,0.022,0.02,113.177,15.014,1.44,60.682,190.313,192.949,90.065,13.28,
2013,September,2.185,0.345,0.021,0.026,106.912,14.367,1.411,58.901,181.591,184.168,88.254,
2013,October,2.171,0.357,0.02,0.029,109.123,15.158,1.483,64.509,190.273,192.849,92.748
The following is the error i get
./calculator.sh: line 16: 0: command not found
0
268.109
I don't understand why echo $currentTransport returns 0 while in the comparison it works and assigns value to maxTransport but throws the error for the same.
Thanks in advance.
Instead of this:
$maxTransport = $currentTransport
Try this:
maxTransport=$currentTransport
The $ in front of a variable gives its contents. By removing the $, the actual variable location of maxTransport is used instead as the destination for the contents of currentTransport.

How can I increment a number in a while-loop while preserving leading zeroes (BASH < V4)

I am trying to write a BASH script that downloads some transcripts of a podcast with cURL. All transcript files have a name that only differs by three digits: filename[three-digits].txt
from filename001.txt
to.... filename440.txt.
I store the three digits as a number in a variable and increment the variable in a while loop. How can I increment the number without it losing its leading zeroes?
#!/bin/bash
clear
# [...] code for handling storage
episode=001
last=440
secnow_transcript_url="https://www.grc.com/sn/sn-"
last_token=".txt"
while [ $episode -le $last ]; do
curl -X GET $secnow_transcript_url$episode$last_token > # storage location
episode=$[$episode+001];
sleep 60 # Don't stress the server too much!
done
I searched a lot and discovered nice approaches of others, that do solve my problem, but out of curiosity I would love to know if there is solution to my problem that keeps the while-loop, despite a for-loop would be more appropriate in the first place, as I know the range, but the day will come, when I will need a while loop! :-)
#!/bin/bash
for episode in $(seq -w 01 05); do
curl -X GET $secnow_transcript_url$episode$last_token > # ...
done
or for just a few digits (becomes unpractical for more digits)
#!/bin/bash
for episode in 00{1..9} 0{10..99} {100..440}; do
curl -X GET $secnow_transcript_url$episode$last_token > # ...
done
You can use $((10#$n)) to remove zero padding (and do calculations), and printf to add zero padding back. Here are both put together to increment a zero padded number in a while loop:
n="0000123"
digits=${#n} # number of digits, here calculated from the original number
while sleep 1
do
n=$(printf "%0${digits}d\n" "$((10#$n + 1))")
echo "$n"
done
for ep in {001..440} should work.
But, as you want a while loop: let printf handle leading zeroes
while (( episode <= last )); do
printf -v url "%s%03d%s" $secnow_transcript_url $episode $last_token
curl -X GET $url > # storage location
(( episode++ ))
sleep 60 # Don't stress the server too much!
done
Will this do?
#!/bin/bash
i=1
zi=000000000$i
s=${zi:(-3)}
echo $s

Unit conversion with Bash

I'm writing a bash script that converts units from a very specific input.
I started out doing simple read and echo statements and was able to get it to read a very specific input derived from declared integers and numbers but I'm having trouble getting it to work with if statements.
Here is my simple code so far:
#!/bin/bash
declare -i n
in=inches
ft=feet
read number in "as" feet
if [ ]; then
echo "$n $in = $[n/12] $ft"
fi
What I want to do now is to create if/else statements to flow according to a number of conditionals dependent on the user input. So I want the user to type something like "24 inches as feet" or "50 yards as inches" and execute its respective output. Right now, I don't really know what to put into the if statements without getting an error like "command not found".
Any help would be appreciated. Thank you.
First, don't use quotes on the variables in your read command.
read number in as feet
Another improvement is to rename the variables to represent what they store. I'm going to replace as with the underscore, a valid but "unreadable" name that emphasizes that it's just a placeholder, and we don't really care about its value.
read value src_unit _ dest_unit
# If the user enters "24 inches as feet", we have:
# value=24
# src_unit=inches
# _=as
# dest_unit=feet
Now, your if statements need to check two things: what is the units of the value, and what unit do we want to convert it to. Here's a template:
if [ "$src_unit" = X ] && [ "$dest_unit" = Y ]; then
# Convert X into Y
fi
You would replace X and Y with the units you are converting from and to, and
the code in the middle would be something like new_value=$(( $value / 12 )), if
converting from inches to feet. Note that bash cannot handle floating-point arithmetic,
which is a topic for another question.
A case statement, as suggested by cwgem:
case "$src_unit-$dest_unit" in
inches-feet)
new_value=$(( $value / 12 ))
;;
gallons-quarts)
new_value=$(( $value * 4 ))
;;
*) echo "I don't know how to convert $src_unit to $dest_unit"
;;
esac
!/bin/bash
echo "enter a number to be converted"
read number
feet=$(($number*12))
inches=$(($number/12))
echo "feet conversion to inches="$feet
echo "inches conversion to feet="$inches
fi

Resources