From owner-freebsd-questions@FreeBSD.ORG Mon Dec 17 15:51:02 2007 Return-Path: Delivered-To: freebsd-questions@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id BA16716A41A for ; Mon, 17 Dec 2007 15:51:02 +0000 (UTC) (envelope-from keramida@ceid.upatras.gr) Received: from igloo.linux.gr (igloo.linux.gr [62.1.205.36]) by mx1.freebsd.org (Postfix) with ESMTP id 41EFE13C43E for ; Mon, 17 Dec 2007 15:51:01 +0000 (UTC) (envelope-from keramida@ceid.upatras.gr) Received: from kobe.laptop (vader.bytemobile-rio.ondsl.gr [83.235.57.37]) (authenticated bits=128) by igloo.linux.gr (8.14.1/8.14.1/Debian-9) with ESMTP id lBHFoI9q021421 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NOT); Mon, 17 Dec 2007 17:50:52 +0200 Received: from kobe.laptop (kobe.laptop [127.0.0.1]) by kobe.laptop (8.14.2/8.14.2) with ESMTP id lBHFoDuj065061; Mon, 17 Dec 2007 17:50:13 +0200 (EET) (envelope-from keramida@ceid.upatras.gr) Received: (from keramida@localhost) by kobe.laptop (8.14.2/8.14.2/Submit) id lBHFoCEP065037; Mon, 17 Dec 2007 17:50:12 +0200 (EET) (envelope-from keramida@ceid.upatras.gr) Date: Mon, 17 Dec 2007 17:50:11 +0200 From: Giorgos Keramidas To: Patrick Dung Message-ID: <20071217155011.GB83628@kobe.laptop> References: <637609.73206.qm@web54307.mail.re2.yahoo.com> MIME-Version: 1.0 Content-Type: text/plain; charset=us-ascii Content-Disposition: inline In-Reply-To: <637609.73206.qm@web54307.mail.re2.yahoo.com> X-Hellug-MailScanner: Found to be clean X-Hellug-MailScanner-SpamCheck: not spam, SpamAssassin (not cached, score=-4.005, required 5, autolearn=not spam, ALL_TRUSTED -1.80, AWL 0.39, BAYES_00 -2.60) X-Hellug-MailScanner-From: keramida@ceid.upatras.gr X-Spam-Status: No Cc: freebsd-questions@freebsd.org Subject: Re: Bash script to find out the summary of user memory usage [not working] X-BeenThere: freebsd-questions@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: User questions List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Mon, 17 Dec 2007 15:51:02 -0000 On 2007-12-17 06:00, Patrick Dung wrote: > I have correction with the script but still doesn't work: > > #!/usr/local/bin/bash > for user in `ps -A -o user | sort | uniq | tail +2` > do > echo "user: $user" > > ps aux -U $user | tail +2 | while read line > do > > mem=`echo $line | awk {'print $4'}` > echo "mem: $mem" > TMPSUMMEM=`awk -v x=$mem -v y=$TMPSUMMEM 'BEGIN{printf > "%.2f\n",x+y}'` > echo "summem: $TMPSUMMEM" > done > echo "finalsummem: $SUMMEM" > export SUMMEM=$TMPSUMMEM > done > > echo "finalsummem: $SUMMEM" There are *many* race conditions in that script. For example, there's no guarantee that once you get a snapshot of the "ps -A -o user" output, then the same users will be listed in the loop you are running for each username. The script is also a bit 'sub-optimal' because it calls ps(1) and parses its output many times (at least as many times as there are users). A much better way to `design' something like this would be to keep a hash of the usernames, and keep incrementing the hash entry for each user as you hit ps(1) output lines. I'm not going to even bother writing a script to use a hash in bash(1), because there are much better languages to work with hashes, dictionaries or even simple arrays. Here's for example a Python script which does what I described: 1 #!/usr/bin/env python 2 3 import os 4 import re 5 import sys 6 7 try: 8 input = os.popen('ps xauwww', 'r') 9 except: 10 print "Cannot open pipe for ps(1) output" 11 sys.exit(1) 12 13 # Start with an empty dictionary. 14 stats = {} 15 16 # Regexp to strip the ps(1) output header. 17 header = re.compile('USER') 18 19 for line in input.readlines(): 20 if header.match(line): 21 continue 22 fields = line.split() 23 if not fields or len(fields) < 4: 24 continue 25 26 (username, mem) = (fields[0], float(fields[3])) 27 value = None 28 try: 29 value = stats[username] 30 except KeyError: 31 pass 32 33 if not value: 34 stats[username] = 0.0 35 stats[username] += mem 36 37 # Print all the stats we have collected so far. 38 keys = stats.keys() 39 if len(keys) > 0: 40 total = 0.0 41 print "%-15s %5s" % ('USERNAME', 'MEM%') 42 for k in stats.keys(): 43 print "%-15s %5.2f" % (k, stats[k]) 44 total += stats[k] 45 # Finally print a grand total of all users. 46 print "%-15s %5.2f" % ('TOTAL', total) It's not the shortest Python script one could write to do what you describe, but I've gone for readability rather than speed or conciseness. Running this script should produce: $ ./foo.py USERNAME MEM% _pflogd 0.10 daemon 0.00 bind 1.10 _dhcp 0.10 keramida 38.60 smmsp 0.10 root 10.10 build 0.00 TOTAL 50.10 $ PS: Yes, you could probably do the same in bash, with sed, awk and a bit of superglue, but I prefer Perl and/or Python for anything which involves something a bit more involved than simple string substitution these days...