system command returns strange value - bash

I have this script
#!/bin/bash
npid=$(pgrep -f procname | wc -l)
echo $npid
if [ $npid -lt 3 ];then
service procname start;
fi
When procname is running, it works fine showing the correct number for $npid. It fails when there isn't any procname running. It should return zero, but it returns npid=3, exactly 3. The same problem I see for ps auxw | grep procname | grep -v grep | wc -l as well.
Something trivially wrong I just couldn't figure out, any suggestions ?
* EDIT
# This returns nothing if therisn't a process name poopit running
pgrep -f poopit
# In the case when no process running, below returns zero if typed on a bash cmd line
pgrep -f poopit | wc -l
# If running,
pgrep -f poopit | wc -l
17
# If running, the script $npid shows
19

Related

Prevent grep from exiting in case of nomatch

Prevent grep returning an error when input doesn't match. I would like it to keep running and not exit with exit code: 1
set -euo pipefail
numbr_match=$(find logs/log_proj | grep "$name" | wc -l);
How could I solve this?
In this individual case, you should probably use
find logs/log_proj -name "*$name*" | wc -l
More generally, you can run grep in a subshell and trap the error.
find logs/log_proj | ( grep "$name" || true) | wc -l
... though of course grep | wc -l is separately an antipattern;
find logs/log_proj | grep -c "$name" || true
I don't know why you are using -e and pipefail when you don't want to have this behaviour. If your goal is just to treat exit code 2 (by grep) as error, but exit code 1 as no-error, you could write a wrapper script around grep, which you
call instead of grep:
#!/bin/bash
# This script behaves exactly like grep, only
# that it returns exit code 0 if there are no
# matching lines
grep "$#"
rc=$?
exit $((rc == 1 ? 0 : rc))

Checking if the same user has 2 terminals open

I'm trying to detect if 1 user has 2 terminal sessions open.
How can this be achieved?
What I tried is the following:
linuxName="$(id -u -n)"
for user in "$#"; do
if echo "$who" | grep -q "$linuxName"; then
echo "[WARNING] You're already logged in";
closeSessionIfNotRoot
fi;
done;
sort count and filter these that have more then 2.
id -u -n | sort | uniq -c | awk '$1 > 2'
#!/usr/bin/bash
linuxName="$(id -un)"
for user in "$#"; do
process_counter=$(pgrep -u "$user" bash 2>&1 | wc -l)
if [[ "$user" == $(who | grep -o "$linuxName") ]]; then
echo "[WARNING] You're already logged in $user"
if [[ "$process_counter" -gt 1 ]]; then
((process_counter--))
pgrep -u "$user" bash | tail -"$process_counter" |
xargs -I % kill -9 %
echo "closed other terminals: $user"
fi
fi
done
script +arguments[in this case, users_names], example, ./script alex zara sophia
Explain answer
Count terminals open by user and save to variable process_counter
process_counter=$(pgrep -u "$user" bash 2>&1 | wc -l)
2>1 REDIRECTS output error to NO SHOW. this in case you wrote a user_name that doesn't exist in the system.
pgrep -u $user bash | wc -l returns number of terminals open. u $user is for returning the process from that user, so this uniq.
pgrep shows the process you are searching for,e.g., pgreg bash returns a lists of process IDs
returns:
363570
369836
370113
this checks if the $user --> ./file alex zara is the same as in terminal $linuxName
[[ "$user" == $(who | grep -o "$linuxName") ]]
grep -o only returns the username alex instead of alex :1 2021-07....
[[ ]] is to make comparisons in bash
xargs is for pasing previous argument to a loop
tail -"$process_counter" | xargs -I % kill -9 %
tail -$process_counter is for showing the last terminals open
363570
369836 #return only this
370113 #return only this
remember that we REST minus 1 to process_counter because we want to leave 1 terminal open, this is done with ((process_counter--)).
So 3 terminals open - 1 terminal = 2 terminals to close.
now we loop with XARGS
363570
369836 #CLOSE THIS kill -9 369836
370113 #CLOSE THIS kill -9 370113
-I arguments tells xargs to assign a variable, represented with symbol %. but this symbol can be anything,like #,$, and then what we want to do is kill each process, so that's why kill -9 %
Check limits.conf which is configuration file for the pam_limits module.
In that file, you can define maxlogins as the maximum number of logins for a user (this limit does not apply to user with uid=0)
See https://www.man7.org/linux/man-pages/man5/limits.conf.5.html.

Count is not Captured in Shell Script Variable

I am trying to capture the number of active processes by running a command and trying to capture the result in a variable of shell script, but unfortunately, nothing is getting captured. The code is as below:
#!/bin/ksh
## Checking whether or not the Previous Build is Completed
count_build_status=`ps -ef | grep BDD_PreCheck.sh | grep -c FT_BGmgmt` | tee -a ${logFile}
echo "The Value of Count Build Status is $count_build_status"
if [[ "${count_build_status}" != "0" ]]
then
echo INFO - The previous build has not ended yet. Please Wait for some time or contact the Administrator | tee -a $logFile
exit 1;
fi
exit 0;
Here, ps -ef | grep BDD_PreCheck.sh | grep -c FT_BGmgmt gives result as 0 if executed individually, but the value stored in 'count_build_status' is null.
Can anyone help?
The problem here is backtick, which does not assign value to the variable when tee'ing is not done inside backtick. It should be included at the end of logFile as in the code below.
#!/bin/bash
logFile=log.txt
## Checking whether or not the Previous Build is Completed
count_build_status=`ps -ef | grep BDD_PreCheck.sh | grep -c FT_BGmgmt | tee -a ${logFile}`
echo "The Value of Count Build Status is $count_build_status"
if [[ "${count_build_status}" != "0" ]]
then
echo INFO - The previous build has not ended yet. Please Wait for some time or contact the Administrator | tee -a $logFile
exit 1;
fi
exit 0;
example: capture output of a cmd-line to a variable and append a logfile with tee:
var=`ps -ef | grep 0 | grep -c 1 | tee -a log`
echo $var

Different output of command substitution

Why does adding | wc -l alters the result as in the following?
tst:
#!/bin/bash
pgrep tst | wc -l
echo $(pgrep tst | wc -l)
echo $(pgrep tst) | wc -l
$ ./tst
1
2
1
and even
$ bash -x tst
+ wc -l
+ pgrep tst
0
++ pgrep tst
++ wc -l
+ echo 0
0
++ pgrep tst
+ echo
pgrep and subshells can have weird interactions, but in this case that's just a red herring; the actual cause is missing double-quotes around the command substitution:
$ cat tst2
#!/bin/bash
pgrep tst | wc -l
echo "$(pgrep tst | wc -l)"
echo "$(pgrep tst)" | wc -l
$ ./tst2
1
2
2
What's going on in the original script is that in the command
echo $(pgrep tst) | wc -l
pgrep prints two process IDs (the main shell running the script, and a subshell created to handle the echo part of the pipeline). It prints each one as a separate line, something like:
11730
11736
The command substitution captures that, but since it's not in double-quotes the newline between them gets converted to an argument break, so the whole thing becomes equivalent to:
echo 11730 11736 | wc -l
As a result, echo prints both IDs as a single line, and wc -l correctly reports that.
The command substitution induces an additional process that has tst in its name, which is included in the input to wc -l.

Bash script checking cpu usage of specific process

First off, I'm new to this. I have some experience with windows scripting and apple script but not much with bash. What I'm trying to do is grab the PID and %CPU of a specific process. then compare the %CPU against a set number, and if it's higher, kill the process. I feel like I'm close, but now I'm getting the following error:
[[: 0.0: syntax error: invalid arithmetic operator (error token is ".0")
what am I doing wrong? here's my code so far:
#!/bin/bash
declare -i app_pid
declare -i app_cpu
declare -i cpu_limit
app_name="top"
cpu_limit="50"
app_pid=`ps aux | grep $app_name | grep -v grep | awk {'print $2'}`
app_cpu=`ps aux | grep $app_name | grep -v grep | awk {'print $3'}`
if [[ ! $app_cpu -gt $cpu_limit ]]; then
echo "crap"
else
echo "we're good"
fi
Obviously I'm going to replace the echos in the if/then statement but it's acting as if the statement is true regardless of what the cpu load actually is (I tested this by changing the -gt to -lt and it still echoed "crap"
Thank you for all the help. Oh, and this is on a OS X 10.7 if that is important.
I recommend taking a look at the facilities of ps to avoid multiple horrible things you do.
On my system (ps from procps on linux, GNU awk) I would do this:
ps -C "$app-name" -o pid=,pcpu= |
awk --assign maxcpu="$cpu_limit" '$2>maxcpu {print "crappy pid",$1}'
The problem is that bash can't handle decimals. You can just multiply them by 100 and work with plain integers instead:
#!/bin/bash
declare -i app_pid
declare -i app_cpu
declare -i cpu_limit
app_name="top"
cpu_limit="5000"
app_pid=`ps aux | grep $app_name | grep -v grep | awk {'print $2'}`
app_cpu=`ps aux | grep $app_name | grep -v grep | awk {'print $3*100'}`
if [[ $app_cpu -gt $cpu_limit ]]; then
echo "crap"
else
echo "we're good"
fi
Keep in mind that CPU percentage is a suboptimal measurement of application health. If you have two processes running infinite loops on a single core system, no other application of the same priority will ever go over 33%, even if they're trashing around.
#!/bin/sh
PROCESS="java"
PID=`pgrep $PROCESS | tail -n 1`
CPU=`top -b -p $PID -n 1 | tail -n 1 | awk '{print $9}'`
echo $CPU
I came up with this, using top and bc.
Use it by passing in ex: ./script apache2 50 # max 50%
If there are many PIDs matching your program argument, only one will be calculated, based on how top lists them. I could have extended the script by catching them all and avergaing the percentage or something, but this will have to do.
You can also pass in a number, ./script.sh 12345 50, which will force it to use an exact PID.
#!/bin/bash
# 1: ['command\ name' or PID number(,s)] 2: MAX_CPU_PERCENT
[[ $# -ne 2 ]] && exit 1
PID_NAMES=$1
# get all PIDS as nn,nn,nn
if [[ ! "$PID_NAMES" =~ ^[0-9,]+$ ]] ; then
PIDS=$(pgrep -d ',' -x $PID_NAMES)
else
PIDS=$PID_NAMES
fi
# echo "$PIDS $MAX_CPU"
MAX_CPU="$2"
MAX_CPU="$(echo "($MAX_CPU+0.5)/1" | bc)"
LOOP=1
while [[ $LOOP -eq 1 ]] ; do
sleep 0.3s
# Depending on your 'top' version and OS you might have
# to change head and tail line-numbers
LINE="$(top -b -d 0 -n 1 -p $PIDS | head -n 8 \
| tail -n 1 | sed -r 's/[ ]+/,/g' | \
sed -r 's/^\,|\,$//')"
# If multiple processes in $PIDS, $LINE will only match\
# the most active process
CURR_PID=$(echo "$LINE" | cut -d ',' -f 1)
# calculate cpu limits
CURR_CPU_FLOAT=$(echo "$LINE"| cut -d ',' -f 9)
CURR_CPU=$(echo "($CURR_CPU_FLOAT+0.5)/1" | bc)
echo "PID $CURR_PID: $CURR_CPU""%"
if [[ $CURR_CPU -ge $MAX_CPU ]] ; then
echo "PID $CURR_PID ($PID_NAMES) went over $MAX_CPU""%"
echo "[[ $CURR_CPU""% -ge $MAX_CPU""% ]]"
LOOP=0
break
fi
done
echo "Stopped"
Erik, I used a modified version of your code to create a new script that does something similar. Hope you don't mind it.
A bash script to get the CPU usage by process
usage:
nohup ./check_proc bwengine 70 &
bwegnine is the process name we want to monitor 70 is to log only when the process is using over 70% of the CPU.
Check the logs at: /var/log/check_procs.log
The output should be like:
DATE | TOTAL CPU | CPU USAGE | Process details
Example:
03/12/14 17:11 |20.99|98| ProdPROXY-ProdProxyPA.tra
03/12/14 17:11 |20.99|100| ProdPROXY-ProdProxyPA.tra
Link to the full blog:
http://felipeferreira.net/?p=1453
It is also useful to have app_user information available to test whether the current user has the rights to kill/modify the running process. This information can be obtained along with the needed app_pid and app_cpu by using read eliminating the need for awk or any other 3rd party parser:
read app_user app_pid tmp_cpu stuff <<< \
$( ps aux | grep "$app_name" | grep -v "grep\|defunct\|${0##*/}" )
You can then get your app_cpu * 100 with:
app_cpu=$((${tmp_cpu%.*} * 100))
Note: Including defunct and ${0##*/} in grep -v prevents against multiple processes matching $app_name.
I use top to check some details. It provides a few more details like CPU time.
On Linux this would be:
top -b -n 1 | grep $app_name
On Mac, with its BSD version of top:
top -l 1 | grep $app_name

Resources