Unable to get correct output in linux script - linux

#!/bin/bash
ac = "(free | grep Mem | awk '{print $4/$2 * 100.0}')"
echo "$ac"
I wanted to get the remaining(free) percentage of RAM usage as output storing in variable for further processing.
Instead m getting $ac output as
total 0.00
Please help

#!/bin/bash
ac="$(free | grep Mem | awk '{print $4/$2 * 100.0}')"
echo "$ac"
works fine for me.
(BTW, awk can do grep's work too:
free | awk '$1=="Mem:" { print 100*$4/$2; }'
)

Related

Identify processes running more than 3 hrs in linux

I want to find out processes running more than 3 hrs, I have written a command for this but it's not returning expected output
ps -u <user> -o pid,stime,pcpu,pmem,etime,cmd --sort=start_time | \
grep <searchString> | grep -v grep| awk '{print $5}' | \
sed 's/:|-/ /g;'| awk '{print $4" "$3" "$2" "$1"}' | \
awk '$1+$2*60+$3*3600+$4*86400 > 10800'
but it's printing the values of etime in output. But expected output is, command should print the values of "pid,stime,pcpu,pmem,etime,cmd"
I am not able to find exact issue with this.
You are executing "awk '{print $5}'" which is taking in the input and printing out only column 5 which in your case is "etime" , everything from this point on is lost.
If your system supports etimes (notice the s on the end), you can easily do this with
ps -eo pid,etimes,etime,comm,user,tty | awk '{if ( $2>10800) print $0}'
on a system not supporting etimes which has a standard output of etime which hh:mm:ss or just mm:ss if no hours have passed
ps -eo pid,etime,comm,user,tty | awk '{seconds_old=10800 ; split($2,a,":",sep) ; if(length(a) < 3) b = (a[1] *60) + (a[2]) ; else b=((a[1]*3600) + (a[2] *60) + (a[3])) ; if(b > seconds_old ) print $0}'
Adjust "seconds_old" to change the age you want to test for:
There are various other methods of doing this using Find for example:
explained here:
https://serverfault.com/questions/181477/how-do-i-kill-processes-older-than-t
However, the solution should match your expected output
Try this:
ps -u <user> -o pid,stime,pcpu,pmem,etime=,cmd --sort=start_time|grep <searchString>|while read z;do tago=$(echo $z|awk '{print $5}'|sed -E 's/(:|-)/ /g'| awk '{print $4+$3*60+$2*3600+$1*86400}');if [ $tago -ge 10800 ];then echo $z;fi;done
It prints only processes >= 10800 secs old.
You can readjust the output further to fit your needs.
Able to find running process for more than 3 hrs with below command.
ps -u <user> -o pid,stime,pcpu,pmem,etime,cmd --sort=start_time |grep -v grep|awk 'substr($0,23,2) > 3'

Division in bash script

I have the following script:
#!/bin/bash
TotalMem=$(top -n 1 | grep Mem | awk 'NR==1{print $4}') #integer
UsadoMem=$(top -n 1 | grep Mem | awk 'NR==1{print $8}') #integer
PorcUsado='scale=2;UsadoMem/TotalMem'|bc -l
echo $PorcUsado
The variable PorcUsado returns empty. I search for the use of bc, but something is wrong...
You're assigning PorcUsado to scale=2;UsadoMem/TotalMem and then piping the output of that assignment (nothing) into bc. You probably want the pipe inside a command substitution, e.g. (using a here string instead of a pipe):
PorcUsado=$(bc -l <<<'scale=2;UsadoMem/TotalMem')
But you'll also need to evaluate those shell variables - bc can't do it for you:
PorcUsado=$(bc -l <<<"scale=2;$UsadoMem/$TotalMem")
Notice the use of " instead of ' and the $ prefix to allow Bash to evaluate the variables.
Also, if this is the whole script, you can just skip the PorcUsado variable at all and let bc write directly to stdout.
#!/bin/bash
TotalMem=$(top -n 1 | grep Mem | awk 'NR==1{print $4}') #integer
UsadoMem=$(top -n 1 | grep Mem | awk 'NR==1{print $8}') #integer
bc -l <<<"scale=2;$UsadoMem/$TotalMem"
Why pipe top output at all? Seems too costly.
$ read used buffers < <(
awk -F':? +' '
{a[$1]=$2}
END {printf "%d %d", a["MemTotal"]-a["MemFree"], a["Buffers"]}
' /proc/meminfo
)
Of course, it can easily be a one-liner if you value brevity over readability.
I think the pipe is the problem try something like this:
PorcUsado=$(echo "scale=2;$UsadoMem/$TotalMem" | bc -l)
i haven't tested it yet but you have to echo the string and pipe the result from echo to bc.
EDIT: Correcting the variable names
You don't need grep or bc, since awk can search and do math all by itself:
top -n 1 -l 1 | awk '/Mem/ {printf "%0.2f\n",$8/$4;exit}'

Subtracting a varibale obtained as output of commands and an integer in shell script

I am finding out the total ideal CPU by using the command
top -bn1 | grep Cpu | awk -F"," '{print $4}' | sed 's/[ \t]*//g' | sed 's/%id//'
Now I want to find out the CPU which is being used so I am trying to subtract the output of the command from 100
So the script looks like :
i=`top -bn1 | grep Cpu | awk -F"," '{print $4}' | sed 's/[ \t]*//g' | sed 's/%id//'`
j=100
k=$(( ${j}-${i} ))
echo $k
When executing this script the output gives an error:
100-93.0 : syntax error: invalid arithmetic operator (error token is ".0 ")
How should I proceed to subtract the derived decimal number from 100 ?
You are getting this error because BASH arithmetic cannot handle floating point numbers and you will get same error while running this command in bash:
((100-93.0))
But you can skip grep, sed and all bash directives. Just a single awk can handle this computation like this:
top -bn1 | awk -F, '/Cpu/ {print 100-$4}'

Append content from variable to log file

I have a script and trying to append the content from my variables to a .log file. I saw on another post(How can I append a variable text to last line of file via command line?) that you can do this using echo "$(cat $FILE)$APPEND" > $FILE. I tried doing that and it’s able to run my script without errors and give the answers I need through terminal but it doesn’t append it to my log file. Can anyone please give me any pointers?
#!/bin/bash
mdate=echo date
mcpu=echo $[100-$(vmstat 1 2 |tail -1|awk ‘{print $15}’)]%
mmem=free | grep Mem | awk ‘{print $3/$2 * 100.0}’
sudo cat /dev/null > /home/daniel/systemstatus.log
echo “$(cat $systemstatus.log)$mdate” >> $systemstatus.log
echo “$(cat $systemstatus.log)$mcpu” >> $systemstatus.log
echo “$(cat $systemstatus.log)$mmem” >> $systemstatus.log
Your code requires several modifications, you cannot assign variables with the output of commands the way you tried to, you need to use command substitutions (var=$(command)) :
mdate=$(date)
mcpu=$(( 100 - $(vmstat 1 2 | tail -1| awk '{print $15}') ))
mmem=$(free | grep Mem | awk '{print $3/$2 * 100.0}')
Your post contains quotes that are not valid for the shell. Use single quotes ' or double quotes " (each has its purpose).
But the logging part is simpler than you thought
logfile=/home/daniel/systemstatus.log
echo "$mdate" >"$logfile" # Overwrites the previous log file
echo "$mcpu" >>"$logfile" # Appends to the log file
echo "$mmem" >>"$logfile" # Appends to the log file
If the only reason for capturing the output of your commands in variables is for logging you can simplify further by redirecting to your log file without capturing the output.
date >"$logfile" # Overwrites the previous log file
echo $(( 100 - $(vmstat 1 2 | tail -1| awk '{print $15}') )) >>"$logfile"
free | grep Mem | awk '{print $3/$2 * 100.0}' >>"$logfile"
You can even put the redirection first, which may be cleaner to the eye depending on your taste :
>"$logfile" date # Overwrites the previous log file
>>"$logfile" echo $(( 100 - $(vmstat 1 2 | tail -1| awk '{print $15}') ))
>>"$logfile" free | grep Mem | awk '{print $3/$2 * 100.0}'
This answer assumes your commands actually work (I have not tested them).
The following should work:
#!/bin/bash
mdate=$(echo date)
mcpu=$(echo $[100-$(vmstat 1 2 |tail -1|awk '{print $15}')]%)
mmem=$(free | grep Mem | awk '{print $3/$2 * 100.0}')
>/home/daniel/systemstatus.log
echo “$(cat $systemstatus.log)$mdate” >> systemstatus.log
echo “$(cat $systemstatus.log)$mcpu” >> systemstatus.log
echo “$(cat $systemstatus.log)$mmem” >> systemstatus.log
Note that if you would like to store the the output of a command in bash you should round the command in $() or `` However, the usage of grave accents is now deprecated.
Furthermore, using cat /dev/null > to empty a file in Linux would basically have the same result as simply using >
As for the issue that you are experiencing, note that appending $ before any word basically makes the script/command to look for a variable with such name instead of the file name.
Regards
Try removing $ before systemstatus
When you type $systemstatus, it means a variable named "systemstatus", not the systemstatus file.

About awk shell and pipe in linux

everyone, I am dealing with a log file which has about 5 million lines, so I use the awk shell in linux
I have to grep the domains and get the highest 100 in the log, so I write like this:
awk '{print $19}' $1 |
awk '{ split($0, string, "/");print string[1]}' |
awk '{domains[$0]++} END{for(j in domains) print domains[j], j}' |
sort -n | tail -n 100 > $2
it runs about 13 seconds
then I change the script like this:
awk 'split($19, string, "/"); domains[string[1]]++}
END{for(j in domains) print domains[j], j}' $1 |
sort -n | tail -n 100 > $2
it runs about 21 seconds
why?
you know one line of awk shell may reduce the sum of cal, it only read each line once, but the time increase...
so, if you know the answer, tell me
When you pipe commands they run in parallel as long as the pipe is full.
So my guess is that in the first version work is distributed among your CPUs, while in the second one all the work is done by one core.
You can verify this with top (or, better, htop).
Out of curiosity, is this faster? (untested):
cut -f 19 -d' ' $1 | cut -f1 -d'/' | sort | uniq -c | sort -nr | head -n 100 > $2

Resources