1

I have this code:

total=0;
ps -u $(whoami) --no-headers | awk {'print $1'} | while read line; 
                          do vrednost=$(pmap $line | tail -n1 | column -t | cut -d" " -f3 | tr "K" " "); 
                          total=$(( vrednost + total ))
                          echo $total 
                          done
                          echo total: $total

As you can see, my code sums usage of all my processes. When I echo my total every time in while, it is working ok, but at the end... When i want total to be a value (echo total: $total) it is still zero. but before (in while) has right value.

simont
  • 68,704
  • 18
  • 117
  • 136
Blaz
  • 11
  • 1
  • Not an answer to your problem, but you can declare `total` to have the 'integer' attribute using `declare -i total=0`, then simply say `total+=$vrednost`. – chepner May 30 '12 at 21:40
  • @chepner: You can also do `(( total += vrednost ))` with or without declaring the variable as an integer. To me, doing this makes it clear that you mean addition and not concatenation. – Dennis Williamson May 31 '12 at 03:17

5 Answers5

1

Okay, pick and choose. You can either do it in BASH or AWK, but don't do both. You've seen a BASH example, here's an AWK example:

ps  -e -o user -o vsz | awk -v USER="$(whoami)" '
    BEGIN {TOTAL = 0}
    END {print "Total is " TOTAL}
    {
        if ($1 == USER) {
            TOTAL += $2
        }
    }
'

Awk is like a programming language that assumes a loop (like perl -n) and processes each line in the file. Each field (normally separated by whitespace) is given a $ variable. The first is $1, the second is $2, etc.

The -v option allows me to define an awk variable (in this case USER) before I run awk.

The BEGIN line is what I want to do before I run my awk script. In this case, initialize TOTAL to zero. (NOTE: This really isn't necessary since undefined variables automatically are given a value of zero). The END line is what I want to do afterwards. In this case, print out my total.

So, if the first field ($1) is equal to my user, I'll add the second field (the vsize) to my total.

All Awk programs are surrounded by {...} and they usually have single quotes around them to prevent shell interpolation of $1, etc.

David W.
  • 105,218
  • 39
  • 216
  • 337
  • 1
    For readability, I prefer putting the `END` block at the ... end. Also, there's no need for an `if` statement - put your condition outside the main block braces: `$1 == USER {TOTAL += $2}` – Dennis Williamson May 31 '12 at 03:20
  • 1
    +1, but note that there's no real need for the BEGIN. Just use `TOTAL + 0` in END to ensure `0` is printed rather than an empty string. Variables are autovivified to 0. – William Pursell May 31 '12 at 14:05
  • I like putting the BEGIN block and END block at the beginning, so people will see them. Otherwise, people miss them. Yes, this is more code than needed. However, I wanted to emphasize the language part. Awk is a programming language with lots of features. I could have simplified the whole thing with a one liner: `ps -u $(whoami) --no-header -o vsz | awk 'END {print "Total is " TOTAL} {TOTAL += $1}'`, but again I wanted to emphasize the Awk language. – David W. May 31 '12 at 15:47
0

Try this

#!/bin/bash
total=0;
for line in `ps -u $(whoami) --no-headers | awk {'print $1'}`; 
do 
  vrednost=$(pmap $line | tail -n1 | column -t | cut -d" " -f3 | tr "K" " "); 
      total=$(( $vrednost + $total ))
      echo $total 
done
echo "total: $total"
golobitch
  • 1,466
  • 3
  • 19
  • 38
  • ps -u $(whoami) --no-headers | awk {'print $1'}; it says that this has an error...i don't know how to fix it :/ – Blaz May 30 '12 at 21:42
  • See [my comment on chepner's answer](http://stackoverflow.com/questions/10824465/why-is-my-code-not-working-as-i-want-it-to#comment14094657_10825147). – Dennis Williamson May 31 '12 at 03:23
0

Let's cut down on the number of extra processes you need :)

declare -i total=0
for size in $( ps -u $(whoami) --no-header -o vsz ); do
    total+=$size
done
echo $total

First, use various options for ps to generate the desired list of process sizes, in kilobytes. Iterate over that list using a bash for-loop, keeping a running total in a parameter declared with the 'integer' attribute for easy arithmetic. The desired sum is now in total, ready for whatever use you need. The sum includes the memory used by the ps process itself.

Using while (Dennis' suggestion) and avoiding process substitution (William's suggestion):

ps -u $(whoami) --no-header -o vsz | { 
  while read -r var; do
    ((total+=$var))
  done
  echo $total
}

(For real one-liner, here's a dc command that I borrowed from https://stackoverflow.com/a/453290/1126841:

ps -u $(whoami) --no-header -o vsz | dc -f - -e '[+z1<r]srz1<rp'

This sum includes the memory used by the ps and dc commands themselves.)

Community
  • 1
  • 1
chepner
  • 497,756
  • 71
  • 530
  • 681
  • Most of the time, it's incorrect to use `for` to iterate over a command substitution. Because of this, it's good to be in the habit of using `while read -r var` instead. – Dennis Williamson May 31 '12 at 03:21
0

Ignacio's answer is fine, but process substitution is not portable. And there is a simpler solution. You need to echo the total in the same subshell in which it is calculated:

... | { while read line; do ...; done; echo total: $total; }
William Pursell
  • 204,365
  • 48
  • 270
  • 300