Using LINUX top command to compute used memory percentage - linux

I am writing the top command output to a text file.
I am trying to write a simple bash script to calculate the percentage of used memory and send an
email if the memory used percentage exceeds, say 90%.
Here is the bash script I have thus far.
#!/bin/bash
top -n 1 -b | grep "Mem" > /home/modadm/top-output.txt
MAXMEM=/home/modadm/top-output.txt | grep "Mem" | cut -c 7-14
USEDMEM=/home/modadm/top-output.txt | grep "Mem" | cut -c 25-31
$USEDPCT='echo $USEDMEM / $MAXMEM * 100 | bc'
$USEDPCT | mail -s "Test Email from MOD Server" test#test.com
When I save and execute the script, I get the error "No such file or directory":
-bash-3.2$ ./memcheck.sh
./memcheck.sh: line 4: =echo $USEDMEM / $MAXMEM * 100 | bc: No such file or directory
Null message body; hope that's ok
-bash-3.2$
Can someone assist? I am a newbie to bash scripting and this is my first script.
Thank you

I will not repeat the content of the other answers; instead I will question the wisdom of parsing the output of top, when all you need is information on the system memory usage.
The output of top is intended for humans and also contains a lot of per-process information that is both unneeded and expensive to produce. The output of free is far more suitable for this particular use.
Secondly, judging by the calculations in your script, you do not seem to understand the way system memory usage is measured on Linux and other Unix-like systems. Contrary to the other OS, the used memory size contains the memory used for disk caches and other buffers. On any system that has been up for some time the free memory tends towards zero - unused memory is wasted memory.
A first step towards finding out the amount of memory used by processes would be to subtract the amount of memory used for buffers from the used memory size. But even that would not be enough on a modern system - even free and top get it wrong to a degree, as mentioned in this older answer of mine.

You have a few problems here.
First, this doesn't do what you want it to do.
USEDMEM=/home/modadm/top-output.txt | grep "Mem" | cut -c 25-31
You can't pipe a filename into a command. You actually want to pipe the contents of the file into the command. You can do that with 'cat'. However, grep is actually designed to search within a file so you can do
USEDMEM=$(grep "Mem" /home/modadm/top-output.txt | cut -c 25-31)
Note that $(cmd) is how you execute a command in a subshell. i.e., you can run some commands to compute the value of a variable in your script. You can also use `cmd` (backticks; usually on the tilde key) but that syntax is less clear.
Again, you probably want to calculate this result in a subshell. Also, don't use $ when assigning to variables.
$USEDPCT='echo $USEDMEM / $MAXMEM * 100 | bc'
This can be rewritten as
USEDPCT=$(echo "scale=3; $USEDMEM / $MAXMEM * 100" | bc)
Finally, you want to pipe the contents of the variable into the mail program. The pipe is expecting a program to be on the left hand side. You do this by echo'ing the value of the variable into the pipe.
echo "$USEDPCT" | mail -s "Test Email from MOD Server" test#test.com
To put everything back together:
#!/bin/bash
top -n 1 -b | grep "Mem" > /home/modadm/top-output.txt
MAXMEM=$(grep "Mem" /home/modadm/top-output.txt | cut -c 7-14)
USEDMEM=$(grep "Mem" /home/modadm/top-output.txt | cut -c 25-31)
USEDPCT=$(echo "$USEDMEM / $MAXMEM * 100" | bc -l)
echo "$USEDPCT" | mail -s "Test Email from MOD Server" test#test.com

Others have pointed out problems with your code, but there are much easier options for this, namely not parsing top output at all. Use /proc/meminfo, and awk - you won't need a temporary file.
$ awk '/MemTotal:/{total=$2} \
/MemFree:/{free=$2} \
END{ \
print "Free mem percent: "(free*100/total); \
print "Used mem percent: "((total-free)*100/total) \
}' /proc/meminfo
Free mem percent: 87.7348
Used mem percent: 12.2652
Pipe that to mail or whatever you want.

Try the following script.
#! /bin/bash
memusage=`top -n 1 -b | grep "Mem"`
MAXMEM=`echo $memusage | cut -d" " -f2 | awk '{print substr($0,1,length($0)-1)}'`
USEDMEM=`echo $memusage | cut -d" " -f4 | awk '{print substr($0,1,length($0)-1)}'`
USEDMEM1=`expr $USEDMEM \* 100`
PERCENTAGE=`expr $USEDMEM1 / $MAXMEM`%
echo $PERCENTAGE | mail -s "Test Email" test#test.com

I have corrected your syntax errors. Pl. note the use of command substitution,
like you have written
MAXMEM=/home/modadm/top-output.txt | grep "Mem" | cut -c 7-14.
This is wrong, you need to write
grep "Mem" /home/modadm/top-output.txt | cut -c 7-14
and then enclose it within backquotes(the key at the lefthand side over tab key) to assign final value to a variable.
Also you have written,
$USEDPCT='echo $USEDMEM / $MAXMEM * 100 | bc'
The dollar sign is used wrongly. $ should be used when you are using the value of
a variable.The quotes should be ` (back quote) and not '(single quote). Backquote means the command will be substituted with the output of the command.
Also for floating point bc needs a scale to be set.
Pl. see the modified code. Hope this helps. Pl. note I have not checked the functionality of the mail command that if it is sending mail or not.
top -n 1 -b | grep "Mem" > /home/modadm/top-output.txt
MAXMEM=`grep "Mem" /home/modadm/top-output.txt | cut -c 7-14`
USEDMEM=`grep "Mem" /home/modadm/top-output.txt | cut -c 25-31`
USEDPCT=`echo "scale=2; $USEDMEM / $MAXMEM * 100" | bc `
echo $USEDPCT | mail -s "Test Email from MOD Server" test#test.com

Related

Set part of grep to variable

mysqladmin proc status | grep "Threads"
Output:
Uptime: 2304 Threads: 14 Questions: 2652099 Slow queries: 0 Opens: 48791 Flush tables: 3 Open tables: 4000 Queries per second avg: 1151.08
I would like to set it so $mysqlthread would output 14 after running echo $mysqlthread
Probably the easiest way is with Perl instead of grep.
mysqladmin proc status | perl -nle'/Threads: (\d+)/ && print $1'
perl -n means "go through each line of input".
perl -l means "print a \n at the end of every print"
perl -e means "here is my program"
/Threads: (\d+)/ means "match Threads: followed by one or more digits. And print $1 means "print the digits I found as denoted by the parentheses around \d+.
Using grep
$ mysqlthread=$(mysqladmin proc status | grep -Po 'Threads: \K\d+')
$ echo "$mysqlthread"
14
There are many ways to solve this. This is one:
mysqladmin proc status | grep "Threads" | tr -s ' ' | cut -d' ' -f4
The tr command with flag -s is used to translate all multiple consecutive spaces into a single space. Then, cut command return the 4th field using a single space as delimiter.
The advantage of piping commands is that one can make this process interactively. And whenever you aren't sure which flag to use, the manual pages are there to help: man grep, man tr, man cut, etc.
Add awk to split the output,
mysqlthread=$(mysqladmin proc status | grep "Threads" | awk '{print $4}')

Why does 'top | grep > file' not work?

I tested the following command, but it doesn't work.
$> top -b -d 1 | grep java > top.log
It doesn't use standard error. I checked that it uses standard output, but top.log is always empty. Why is this?
By default, grep buffers output which implies that nothing would be written to top.log until the grep output exceeds the size of the buffer (which might vary across systems).
Tell grep to use line buffering on output. Try:
top -b -d 1 | grep --line-buffered java > top.log
In my embedded machine, grep hadn't the --line-buffered option. So I used this workaround for my myself:
while :;do top -b -n 1 | grep java >> top.log;done &
By this way I could have a running monitor in the background for a program like "java" and keep all results in the file top.log.

How do I grep multiple lines (output from another command) at the same time?

I have a Linux driver running in the background that is able to return the current system data/stats. I view the data by running a console utility (let's call it dump-data) in a console. All data is dumped every time I run dump-data. The output of the utility is like below
Output:
- A=reading1
- B=reading2
- C=reading3
- D=reading4
- E=reading5
...
- variableX=readingX
...
The list of readings returned by the utility can be really long. Depending on the scenario, certain readings would be useful while everything else would be useless.
I need a way to grep only the useful readings whose names might have have nothing in common (via a bash script). I.e. Sometimes I'll need to collect A,D,E; and other times I'll need C,D,E.
I'm attempting to graph the readings over time to look for trends, so I can't run something like this:
# forgive my pseudocode
Loop
dump-data | grep A
dump-data | grep D
dump-data | grep E
End Loop
to collect A,D,E as that would actually give me readings from 3 separate calls of dump-data as that would not be accurate.
If you want to save all result of grep in the same file, you can just join all expressions in one:
grep -E 'expr1|expr2|expr3'
But if you want to have results (for expr1, expr2 and expr3) in separate files, things are getting more interesting.
You can do this using tee >(command).
For example, here I process the same pipe with thre different commands:
$ echo abc | tee >(sed s/a/_a_/ > file1) | tee >(sed s/b/_b_/ > file2) | sed s/c/_c_/ > file3
$ grep "" file[123]
file1:_a_bc
file2:a_b_c
file3:ab_c_
But the command seems to be too complex.
I would better save dump-data results to a file and then grep it.
TEMP=$(mktemp /tmp/dump-data-XXXXXXXX)
dump-data > ${TEMP}
grep A ${TEMP}
grep B ${TEMP}
grep C ${TEMP}
You can use dump-data | grep -E "A|D|E". Note the -E option of grep. Alternatively you could use egrep without the -E option.
you can simply use:
dump-data | grep -E 'A|D|E'
awk '/MY PATTERN/{print > "matches-"FILENAME;}' myfile{1,3}
thx Guru at Stack Exchange

What is this Bash (and/or other shell?) construct called?

What is the construct in bash called where you can take wrap a command that outputs to stdout, such that the output itself is treated like a stream? In case I'm not describing that so well, maybe an example will do best, and this is what I typically use it for: applying diff to output that does not come from a file, but from other commands, where
cmd
is wrapped as
<(cmd)
By wrapping a command in such a manner, in the example below I determine that there a difference of one between the two commands that I am running, and then I am able to determine that one precise difference. What is the construct/technique of wrapping a command as <(cmd) called? Thanks
[builder#george v6.5 html]$ git status | egrep modified | awk '{print $3}' | wc -l
51
[builder#george v6.5 html]$ git status | egrep modified | awk '{print $3}' | xargs grep -l 'Ext\.define' | wc -l
50
[builder#george v6.5 html]$ diff <(git status | egrep modified | awk '{print $3}') <(git status | egrep modified | awk '{print $3}' | xargs grep -l 'Ext\.define')
39d38
< javascript/reports/report_initiator.js
ADDENDUM
The revised command using the advice for using git's ls-file should be as follows (untested):
diff <(git ls-files -m) <(git ls-files -m | xargs grep -l 'Ext\.define')
It is called process substitution.
This is called Process Substitution
This is process substitution, as you have been told. I'd just like to point out that this also works in the other direction. Process substitution with >(cmd) allows you to take a command that writes to a file and instead have that output redirected to another command's stdin. It's very useful for inserting something into a pipeline that takes an output filename as an argument. You don't see it as much because pretty much every standard command will write to stdout already, but I have used it often with custom stuff. Here is a contrived example:
$ echo "hello world" | tee >(wc)
hello world
1 2 12

How to get the total physical memory in Bash to assign it to a variable?

How can I get the total physical memory in bytes of my Linux PC?
I need to assign it to a bash script variable.
grep MemTotal /proc/meminfo | awk '{print $2}'
The returned number is in KB
phymem=$(awk -F":" '$1~/MemTotal/{print $2}' /proc/meminfo )
or using free
phymem=$(LANG=C free|awk '/^Mem:/{print $2}')
or using shell
#!/bin/bash
while IFS=":" read -r a b
do
case "$a" in
MemTotal*) phymem="$b"
esac
done <"/proc/meminfo"
echo $phymem
I came up with this one under the assumption, that the physical memory will be the first number in free's output:
free -m | grep -oP '\d+' | head -n 1
This allows you to configure free to output the unit you want (-m, -g, ...) and it is independent of the system language (other answers depend on the "Mem:" string in free's output which may change based on the language).
How about
var=$(free | awk '/^Mem:/{print $2}')
I'll try to make this answer self explanatory, just keep up with me.
To get the description of memory, you can use the free utility :
free -t
Output (in KB):
total used free shared buff/cache available
Mem: 8035900 3785568 324984 643936 3925348 3301908
Swap: 3906556 271872 3634684
Total: 11942456 4057440 3959668
To extract all of these values from this output in a single column :
free -t | grep -oP '\d+'
Output (in KB):
8035900
3866244
266928
650348
3902728
3214792
3906556
292608
3613948
11942456
4158852
3880876
Note : Minute difference can be there in values, which doesn't matter most of the times.
If you just want to get the total physical memory (mem+swap), it is the 10th value in above output :
free -t | grep -oP '\d+' | sed '10!d'
Output (on my PC):
11942456
Note: All the above outputs are in Kilo Bytes. If you want in Mega Bytes or Giga Bytes just append -m or -g after -t in
above free commands respectively.
For Example :
free -t -g | grep -oP '\d+' | sed '10!d'
Output (in Giga Bytes on my PC) :
11
Silly inline python version, which looks overly complicated, but is actually kind of useful.
freemem=$(echo -e 'import re\nmatched=re.search(r"^MemTotal:\s+(\d+)",open("/proc/meminfo").read())\nprint(int(matched.groups()[0])/(1024.**2))' | python)
It returns the memory in GB.
If someone need a human readable:
var=$(free -h | awk '/^Mem:/{print $2}')
result:
1.9G

Resources