3

I'm a bit confused by the done < $1 notation.

I'm trying to write a program "sumnums" that reads in a file called "nums" that has a couple rows of numbers. Then it should print out the rows of the numbers followed by a sum of all the numbers.

Currently I have:

#!/bin/bash
sum=0;
while read myline
do  
  echo "Before for; Current line: \"$myline\""
done
for i in $myline; do  
  sum=$(expr $sum + $i)
done < $1    
echo "Total sum is: $sum"

and it outputs the list of the numbers from nums correctly then says ./sumnums: line 10: $1: ambiguous redirect, then outputs Total sum is: 0.

So somehow it isn't adding. How do I rearrange these lines to fix the program and get rid of the "ambiguous redirect"?

8
  • How are you invoking your script? Either call it as ./sumnum nums or change done < $1 to done < nums. Commented Apr 25, 2017 at 2:15
  • cat nums | ./sumnums Commented Apr 25, 2017 at 2:28
  • script needs the file as the argument. Commented Apr 25, 2017 at 2:51
  • Uhhh. You'll want to redirect stdin for the while read loop, not the for loop. Commented Apr 25, 2017 at 2:57
  • BTW, consider running scripts through shellcheck.net as a matter of course before asking about them here. Commented Apr 25, 2017 at 3:04

2 Answers 2

4

Assuming your filename is in $1 (that is, that your script was called with ./yourscript nums):

#!/bin/bash

[[ $1 ]] || set -- nums  ## use $1 if already set; otherwise, override with "nums"

sum=0
while read -r i; do      ## read from stdin (which is redirected by < for this loop)
  sum=$(( sum + i ))     ## ...treat what we read as a number, and add it to our sum
done <"$1"               ## with stdin reading from $1 for this loop
echo "Total sum is: $sum"

If $1 doesn't contain your filename, then use something that does contain your filename in its place, or just hardcode the actual filename itself.

Notes:

  • <"$1" is applied to a while read loop. This is essential, because read is (in this context) the command that actually consumes content from the file. It can make sense to redirect stdin to a for loop, but only if something inside that loop is reading from stdin.
  • $(( )) is modern POSIX sh arithmetic syntax. expr is legacy syntax; don't use it.
Sign up to request clarification or add additional context in comments.

Comments

0

awk to the rescue!

awk '{for(i=1;i<=NF;i++) sum+=$i} END{print "Total sum is: " sum}' file

bash is not the right tool for this task.

2 Comments

Where is the "done <" documented? What is the meaning of the "<" in bash? Does it have a name such as "redirect," under which its documentation can be found?
@JacobWegelin If I knew a simple explanation I would provide it, but I'm figuring it out too. Here's a link to some documentation. Find this line: done < <(route -n) # ^ ^ First < is redirection, second is process substitution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.