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