| Forum Home | ||||
| Press F1 | ||||
| Thread ID: 123904 | 2012-03-24 08:23:00 | Unix scripting help | pcuser42 (130) | Press F1 |
| Post ID | Timestamp | Content | User | ||
| 1266642 | 2012-03-24 08:23:00 | (I'm willing to bet some of you never thought I'd post a thread about a non-Windows OS. :p) For a computer science assignment, I'm trying to write a script that reads numbers from the file "foo" (and only this file :stare:), adds up each number in each line and displays the result. I've got it mostly working, except for the fact that at the end it outputs 0 as the sum, when Unix is actually adding the numbers... #!/bin/bash cat foo | while read line do sum=0 echo $line | tr " " "\n" > temp.txt cat temp.txt | while read num do sum=$(($sum+$num)) echo Sum $sum done echo $sum # rm temp.txt done The echo Sum $sum command just after the actual addition outputs the correct sum for each iteration, yet echo $sum (where I actually want the output) is outputting 0. Am I missing something with the variable scope or something else (like sleep)? :waughh: |
pcuser42 (130) | ||
| 1266643 | 2012-03-24 08:34:00 | You set the sum=0 every time a line is read. | fred_fish (15241) | ||
| 1266644 | 2012-03-24 08:37:00 | You set the sum=0 every time a line is read. That's correct, I want to add the numbers on each line. |
pcuser42 (130) | ||
| 1266645 | 2012-03-24 20:00:00 | It's something to do with the way you're reading the line of text in the inner loop. If I change your inner loop to something simpler, it works - my gut feel is it's something to do with the "while read num" although I don't know exaclty why. [somebody@myserver ~]$ cat pcuser42.sh #!/bin/bash cat foo | while read line do sum=0 for num in $line do sum=$(($sum+$num)) echo Sum $sum done echo $sum # rm temp.txt done [somebody@myserver ~]$ ./pcuser42.sh Sum 1 Sum 24 Sum 117 Sum 161 Sum 216 216 Sum 2 Sum 25 Sum 118 Sum 162 Sum 217 217 Sum 3 Sum 26 Sum 119 Sum 163 Sum 218 218 |
somebody (208) | ||
| 1266646 | 2012-03-24 20:41:00 | It's something to do with the way you're reading the line of text in the inner loop. If I change your inner loop to something simpler, it works - my gut feel is it's something to do with the "while read num" although I don't know exaclty why. [somebody@myserver ~]$ cat pcuser42.sh #!/bin/bash cat foo | while read line do sum=0 for num in $line do sum=$(($sum+$num)) echo Sum $sum done echo $sum # rm temp.txt done [somebody@myserver ~]$ ./pcuser42.sh Sum 1 Sum 24 Sum 117 Sum 161 Sum 216 216 Sum 2 Sum 25 Sum 118 Sum 162 Sum 217 217 Sum 3 Sum 26 Sum 119 Sum 163 Sum 218 218 Thanks for that, works perfectly now :D (not quite sure why the assignment said temporary files might be needed now...) |
pcuser42 (130) | ||
| 1266647 | 2012-03-24 22:00:00 | I guess it depends what you've been taught so far - there are numerous ways of achieving the same thing. I generally try to avoid using temporary files unless absolutely necessary, since doing stuff in memory is faster, although I must admit that in some situations it's just easier to do it that way. You could possibly write the same thing with an awk script which might save you a couple of lines :) In any case, once you get the hang of writing scripts, it makes automating tasks really easy. At work I frequently scripts to automate mundane tasks, so I can get on with doing the interesting stuff. |
somebody (208) | ||
| 1266648 | 2012-03-27 07:35:00 | The echo Sum $sum command just after the actual addition outputs the correct sum for each iteration, yet echo $sum (where I actually want the output) is outputting 0. Am I missing something with the variable scope or something else (like sleep)? :waughh:It's a scope problem related to the way you're using those inner loops. Because you're piping the output of cat into a while loop, that entire while loop is running as a subshell - it's a completely separate process with its own environment. The script as you've written it is actually three completely separate scripts, and will run with up to three concurrent processes. The important thing to remember here is that the pipe operator spawns a new process - it's actually a way of doing inter-process communication; you can't use it to manipulate data within a single process. I also agree with Somebody about that temporary file - avoid them where possible; files are (usually) much slower than in-memory operations. |
Erayd (23) | ||
| 1266649 | 2012-03-27 14:43:00 | If you're curious, this is what your script looks like if you refactor it to remove inter-process pipes, but leave everything else alone. Note that as this is now just one script (rather than three), it will run the way you originally expected it to run (i.e. it will echo the sum, rather than 0). #!/bin/bash while read line do sum=0 tr " " " \n " <<< " $line " > temp.txt while read num do sum=$(($sum+$num)) echo Sum $sum done < temp.txt echo $sum # rm temp.txt done < foo |
Erayd (23) | ||
| 1266650 | 2012-03-27 19:37:00 | If you're curious, this is what your script looks like if you refactor it to remove inter-process pipes, but leave everything else alone. Note that as this is now just one script (rather than three), it will run the way you originally expected it to run (i.e. it will echo the sum, rather than 0). #!/bin/bash while read line do sum=0 tr " " " \n " <<< " $line " > temp.txt while read num do sum=$(($sum+$num)) echo Sum $sum done < temp.txt echo $sum # rm temp.txt done < foo Hmm interesting... not quite sure what the less than (<) signs do though? |
pcuser42 (130) | ||
| 1266651 | 2012-03-28 00:08:00 | Hmm interesting... not quite sure what the less than (<) signs do though?They're file pipes - in this case, they're reading the file contents and piping it into the while loop. The ' <<< ' variation of this will pipe the next argument string, rather than a file. You can also use '<<' (heredoc pipe) - this allows you to enter unquoted multiline text followed by an arbitrary terminating string: #!/bin/bash while read line do sum=0 tr " " " \n " <<< " $line " > temp.txt while read num do sum=$(($sum+$num)) echo Sum $sum done < temp.txt echo $sum # rm temp.txt done << eot 2 2 2 3 3 3 4 5 6 1 2 3 eot |
Erayd (23) | ||
| 1 | |||||