3

OK, so I love my awk, but yeah, it has precision issues, and unfortunately there's no easy way for me to install the multi-precision extensions known as gawkextlib.

What I am doing is, I am working through problems on rosalind.info using shell one-liners. I find that it's not difficult for me to perform the required computations on DNA/RNA strands using these shell one-liners within the five minute timeframe set by the website.

Anyway, I'm stuck on this problem, but I always want to sharpen my knowledge of linux tools. In this case, I need to call bc from awk.

The bc command should be:

bc <<< "scale=1000; $1/$2"

Where $1 and $2 are the two columns of text I am working with in awk.

The awk command is derived from some shell functions I wrote:

nucleic-line () {
sed 's/\(.\)/\1\n/g' < $@
}

gc-numeric-count () {
n=$(nucleic-line $@ | wc -l)
m=$(nucleic-line $@ | grep -v "[AT]" | wc -l)
echo $m $n
}
export -f gc-numeric-count

column-percent-count () {
for f in $@; do gc-numeric-count $f; done | awk '{a = $1/$2 | print a * 100}'
}

For my purposes, awk '{a = $1/$2 | print a * 100}' is not precise enough. It gets the percentage of guanine and cytosine correct, but I need it to more decimal places than awk can deliver. Like I said, I unfortunately can't install the gawkextlib. I need arbitrary precision, so I need to use bc. But I also want to be able to process columns, like I can in awk.

So, how can I amend the last line of the last expression to use the bc command on $1 and $2?

3 Answers 3

3

Your problem is that you're taking the shell for what it's not: a programming language. A shell is before all a command line interpreter. Shell scripts are scripts. It you're implementing the logic, the algorithm of your problem in shell syntax, then you're going down the wrong road.

There are obvious problems in your code like unquoted variables. But all in all, it just feels ugly to run so many commands (as a shell is a tool to run commands, not a programming languages) just to find the proportion of characters other than A and T in a file.

Also, awk uses 64bit float numbers internally. Are you sure you need more precision than that. If those numbers are to be used by something that has more precision than that, can't you use that something to do the whole thing?

To answer your question, you'd do:

$ echo 1 3 | awk -vRS= '{("echo scale=300\\;" $1 "/" $2 "|bc -l") | getline; print}'
.3333333333333333333333333333333333333333333333333333333333333333333\
33333333333333333333333333333333333333333333333333333333333333333333\
33333333333333333333333333333333333333333333333333333333333333333333\
33333333333333333333333333333333333333333333333333333333333333333333\
33333333333333333333333333333

But you can easily see how pointless that is: awk running a shell and two commands for each line of input, reading their output and printing it again... Even the outer shell could have done as good a job with less fuss.

A slightly less silly to approach it, if you still want to use awk, would be:

echo 1 3 | awk 'BEGIN{print "scale=300"}{print $1"/"$2}' | bc

That time, only one awk and one bc command.

It's quite obvious to me that you need a real programming language here (perl, ruby, python come to mind). You can call the interpreter for that programming language from a shell script, but please only once: just one invocation should be enough to do the whole thing.

7
  • thank you for your advice. in fact i know already, Stephane, that programming languages are the tool for this. but when it comes down to it i'm just much quicker at thinking of a shell solution than a programming solution, and there's a five minute time limit on the site for each problem. there will probably be problems that are too weird to do in the shell, and would take less time in a language, so i'm sort of waiting for that to happen...
    – ixtmixilix
    Dec 18, 2012 at 21:16
  • i get an error when i run the command you pasted above: /bin/sh: 1: Syntax error: "|" unexpected
    – ixtmixilix
    Dec 18, 2012 at 21:20
  • 1
    I suspect your awk is mawk which has precedence issues with the | operator. I've edited it so it's portable to mawk as well. Dec 18, 2012 at 21:41
  • 1
    @ixtmixilix - I don't think the intent of that site is to write the whole program within the 5 minute time limit. Rather, the time limit ensures you consider your solution before downloading the test data, and prevents many brute force attempts. This is covered in the Rosalind FAQ. Dec 18, 2012 at 22:10
  • thank you, Stephane... as far as i can tell, the command doesn't work with columns of data, though...
    – ixtmixilix
    Dec 18, 2012 at 23:12
0

Without knowing the rest of your script (I don't know whether you're relying on the return values $m $n somewhere else) - have you considered this?

gc-numeric-count () {
  n=$(nucleic-line $@ | wc -l)
  m=$(nucleic-line $@ | grep -v "[AT]" | wc -l)
  echo "scale=1000;${m}/${n"}
}
export -f gc-numeric-count

column-percent-count () {
  for f in $@; do gc-numeric-count $f | bc -l; done 
}
0

I don't see any problem in passing variables. Plus, GNU dc (included in bc) is far more easier for embedded calc, less piping and Reverse-Polish:

print '355 113' | awk -vRS='' '{"dc -e \"1000k"$1" "$2"/pq\"" | getline; print; close(dc)}

Agree with Stephane that shell do the clue work, awk do the formatting work, and calculation shall go dc

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .