LinuxQuestions.org
Download your favorite Linux distribution at LQ ISO.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 10-20-2012, 10:53 PM   #1
januka
LQ Newbie
 
Registered: Sep 2010
Posts: 18

Rep: Reputation: 0
Arithmetic (bc) within for loop


I've written the following script to extract a certain numeric field after reading a file (e.g. 50 lines long) line by line, and then perform a simple subtraction on that particular field using bc. This particular line does not seem to work and I have a hard time understanding why. Any help much appreciated!

PS. I could use awk to do it but would like to know what's wrong with bc in this case

#! /bin/sh

start=1
step=1
end=50
#input=datafile with 50 lines

for data in `seq $start $step $stop`
do

line=`cat datafile | gawk -vcount="$data" 'NR=count {print}'`
# this prints the line that's being read

r=`echo $line | gawk '{print $2}'`
# extracts the field, I can echo r and get the desired output

radius=`echo "300 - $r" | bc`
# this line doesn't work, why?
done

Last edited by januka; 10-20-2012 at 11:03 PM.
 
Old 10-21-2012, 02:22 AM   #2
dru8274
Member
 
Registered: Oct 2011
Location: New Zealand
Distribution: Debian
Posts: 105

Rep: Reputation: 37
A couple of small problems...

In the script, you have declared end=50, and then below used "seq $start $step $stop" - so it looks like you meant to use $end instead of $stop, which was so far undefined.

And then in the first gawk, I think that 'NR=count {print}' works better as 'NR==count {print}' - that extra "=" sign is necessary for an equality.

And because that first gawk statement isn't working properly, the values of $line and $r are not as expected either, leading to an error from bc. At least, that's how it appears to me.

Such that finally, when that gawk was fixed, then the script basically works.
Code:
#!/bin/sh

# Create a testfile
>datafile;
for var in `seq 1 1 50`; do
    echo -e "$var\t$(($var*2))\t$(($var*4))" >>datafile
done

start=1
step=1
end=50

for data in `seq $start $step $end`
do
    line=`cat datafile | gawk -vcount="$data" 'NR==count {print}'`
    r=`echo $line | gawk  '{print $2}'`
    radius=`echo "scale=0 ; 300 - $r" | bc`
    echo $r $radius
done
Happy with ur solution... then tick "yes" and mark as Solved!

Last edited by dru8274; 10-21-2012 at 02:27 AM.
 
Old 10-21-2012, 06:15 AM   #3
David the H.
Bash Guru
 
Registered: Jun 2004
Location: Osaka, Japan
Distribution: Arch + Xfce
Posts: 6,852

Rep: Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037Reputation: 2037
Please use ***[code][/code]*** tags around your code and data, to preserve the original formatting and to improve readability. Do not use quote tags, bolding, colors, "start/end" lines, or other creative techniques.


Does this have to be a posix-compatible sh script, or can you use bash or another more advanced shell with features that can make things easier?


In any case, the first bit of advice I have is that $(..) is highly recommended over `..`. This is a fully posix-compatible feature, so there's really no reason to ever use backticks anymore.

Second, formatting! Clean, consistent formatting makes code readable and more easily debuggable. Be liberal with whitespace; indent all your sub-commands evenly, and separate logical sections with empty lines. Never just line everything up on the left edge. Add comments anywhere the code isn't completely obvious (and remember, what seems obvious to you now will not be so a year or so down the line).

One non-posix feature I would recommend is to use the c-style for loop, instead of seq.
If you simply want to read every line of the file, use a while+read loop instead.

Code:
#!/bin/bash

start=1
step=1
end=50

datafile=/path/to/datafile.txt


for (( data=start ; data<=end ; data+=step )); do

	r=$( gawk -v ln="$data" 'NR==ln { print $2 }' "$datafile" )

	radius=$( echo "300 - $r" | bc )

	echo "$radius"

done
Now a few more notes about the original script:

1)
Another option, instead of a for loop, would be a while loop:

Code:
data=$start
while (( data <= stop )); do	#use [ "$data" -le "$stop" ] for posix compatibility
	<commands>
	data=$(( data + step ))
done
2)
It's not a good idea to hard-code filenames and similar data sources in a script.
Set it up in a variable at the top and use the variable in the code itself.


3)
Code:
line=`cat datafile | gawk -vcount="$data" 'NR=count {print}'`
as mentioned, in awk "=" sets a variable, "==" tests it.
Also notice the Useless Use Of Cat.

Code:
line=$( gawk -vcount="$data" 'NR == count {print}' "$datafile" )
sed is easier to use when extracting lines anyway.
Code:
line=$( sed -n "${data} p" "$datafile" )
4)
Code:
r=`echo $line | gawk '{print $2}'`
If using bash, a herestring can be used instead of echo.
And cut can usually be used instead of awk for simple field extraction.

Code:
r=$( gawk '{print $2}' <<<"$line" )
r=$( cut -t ' ' -f2 <<<"$line" )	#assumes space-delimited fields
Built-in parameter substitution may be even better.

(This example is a guess since I don't know what the line actually looks like,
but assuming space-delimited fields...)

Code:
r=${line#* }
r=${r%% *}
There are other string manipulation techniques available too, it the above isn't satisfactory. Using read, for example:

Code:
read _ r _ <<<"$line"	#_ is a disposable variable for dumping the data we don't need.

But as I showed above, the whole of the above section can be condensed into a single awk command anyway.

Last edited by David the H.; 10-21-2012 at 06:19 AM. Reason: missing tag
 
1 members found this post helpful.
Old 10-22-2012, 07:26 PM   #4
januka
LQ Newbie
 
Registered: Sep 2010
Posts: 18

Original Poster
Rep: Reputation: 0
Thanks

Dru8274: Sorry, those are typos. In the actual code they were set right, but thanks for your answer.
David the H: Thanks, I will implement your suggestions. Thanks !
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
how to loop over text file lines within bash script for loop? johnpaulodonnell Linux - Newbie 9 07-28-2015 03:49 PM
[SOLVED] Bash - While Loop reading from two lists simultaneously - nested while loop wolverene13 Programming 11 10-01-2011 05:00 PM
[SOLVED] arithmetic in bash xeon123 Linux - Newbie 9 04-23-2011 09:39 AM
bash, for loop, and arithmetic rmount Programming 13 05-23-2007 01:27 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 07:54 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration