Sort PS command by etime - sorting

So I am trying to develop a script that will find specfic processes, and kill the one that has been running the longest. Trying to get the command sorted by elapsed time is becoming the issue. Here is the command i am running, i know there are lots of | and likely a cleaner why to do all this, but I am fairly new to using awk.
ps -eo pid,cmd,stat,etime --sort=etime | grep cassi32 | awk '$3 == "/rESTECH"' | awk '$4 == "S"'
and the output i get is this.
5703 cassi32 /rESTECH S 00:40
65504 cassi32 /rESTECH S 1-21:45:39
65520 cassi32 /rESTECH S 03:21:39
65521 cassi32 /rESTECH S 3-15:02:37
65531 cassi32 /rESTECH S 1-21:44:39
As you can see the etime column doesnt not appear to be in any particular order, and it is sorting by PID.
Any ideas on how to get this sorted by etime. once that is finished i can take care of the kill part.

The version of ps that you have (as well as one I'm testing with) seems to have issues properly sorting on some subset of time-based keys. This appears to do what you want, though:
ps -eo pid,cmd,stat,etime --sort start_time | awk '$2 == "cassi32" && $3 == "/rESTECH" && $4 == "S"'
Sorting by start_time seems a bit more reliable, at least on my system, and it's directly related to etime or elapsed time.

You can reduce your command line to just:
ps -eo pid,cmd,stat,etime --sort=etime |
awk '/cassi32/ && ($3=="/rESTECH") && ($4=="S")'
but the ONLY thing above doing any kind of ordering is your ps command so if that isn't producing the order you want then read your ps man page to figure out what options you really should be using.
I have access to several UNIX machines but none of them have a ps that supports the options you are using so I can't test it.

Can you pipe to sort at the end?, and possible simplify the pipline using just one awk:
e.g.
ps -eo args,pid,etime | awk '$2 == "cassi32" && $3 == "/rESTECH" && $4 == "S"' | sort -k 5

Related

How to filter processes by MEM% in Bash?

I've been given an assignment to print processes that are using 'x%' or more memory every 10 seconds. The x% will come from an argument when executing the file.
(E.G. ./processes.sh 8 will print all processes using 8% or more memory every 10 seconds.)
I have no clue how to sort the processes by a specific memory parameter. I know how to sort the processes in ascending or descending order, just not how to print only the select processes that meet my criteria. Here's a snippet of my code:
processes()
{
while :
do
date
echo "Processes occupying $1% of memory: "
ps -o pid,user,%mem ax|sort -n -b -k3 -r|pgrep -f1 "$1"
sleep 10
done
}
processes $1
I thought pgrep might be able to do it, but I think I either formatted it wrong, or it just doesn't work.
Ideally, the output should look like this:
Processes occupying 8% or more memory:
11452 kelly1653 13.6%
93612 buckley0003 29.6%
Any hints for me? I'm really frustrated. Thanks in advance!
This should be all you need:
ps --no-headers -o pid,user,%mem |
awk -v "arg=$1" '$3 >= arg {print $0 "%"}' |
sort -nk 3
Use awk to select lines that have the third field value your argument or more, and to add the % which is missing from the ps output. Then sort numerically according to the third field.
Or with ps doing the sorting:
ps --no-headers -o pid,user,%mem --sort %mem |
awk -v "arg=$1" '$3 >= arg {print $0 "%"}'

Using ps and awk to get pid, then killing it

I have written a very short script that gets pid of $1 then kills the process. I have a few questions / requests for improvement. (OSX)
#!/bin/bash
getpid=$(ps aux -O start | grep -i "$1" | grep -v grep | grep -v $0 | awk '{ if (NR==1) {print $2}}')
kill $getpid
For example:
$ ~/killpro.sh java
Which kills the most recent instance of java.
Or:
$ ~/killpro.sh chrome
To kill Google Chrome, obviously.
First, is there any practical way to get this strung together into a single line, using read -p perhaps, so that it can in turn be aliased and become something like:
$ killpro
$ Enter name: chrome
$
Not that it's significantly easier, in fact it's slightly more work, but it bypasses the need to type a path and shell extension which is desirable.
Second, is there anything fundamentally wrong with my initial approach / am I missing something obvious that negates the need for any this?
Any feedback is appreciated.
Thanks in advance, I'm (as you can tell) new to this.
My advice is to use pkill instead. From the manual page:
NAME
pgrep, pkill -- find or signal processes by name
Most scripts with grep | awk are a useless use of grep and should be refactored to use just Awk.
#!/bin/bash
# Maybe add a case for $# > 1 and quit with an error message
# Or maybe loop over arguments if there is more than one
case $# in 0) read -p "Process name? " proc;; *) proc=$1;; esac
# Maybe your ps aux doesn't have the command in $11 -- adapt
kill $(ps aux -O start |
awk -v name="$proc" 'NR>1 && tolower($11) ~ tolower(name) {print $2}')
Your comments indicate you seek to kill the "latest" process (by which metric?) but your code, and this code, attempts to kill all the instances. If you want the most recently started process, the PID is not a good indicator; you should look at process start times etc. The output of ps is heavily system-dependent so you should probably post a separate question with more details about your OS if you need help figuring that out.
This particular problem regularly gets beaten to death several times a month here, so you should probably look at other implementations for inspiration. My recommendation would be "don't do that" but since you already did, I suppose it makes sense to point out the flaws and attempt to solidify the code. But if you don't have a compelling reason why you cannot install pkill, you should probably just use that (and if you do, using a properly debugged and tested script is way better than writing your own from scratch).
You could just use awk alone with getline to call out to ps and get the data you want:
awk -vv_kp="bash" 'BEGIN{ cmd="ps -axu -O T | grep "v_kp" |head -1"; cmd | getline var; close(cmd); split(var,var2," ");print var2[2];}'
or less awk, more shell:
awk -vv_kp="bash" 'BEGIN{ cmd="ps -axu -O T | grep "v_kp" |head -1|cut -d\" \" -f2"; cmd | getline var; close(cmd); print var;}'

grep: compare string from file with another string

I have a list of files paths that I need to compare with a string:
git_root_path=$(git rev-parse --show-toplevel)
list_of_files=.git/ForGeneratingSBConfigAlert.txt
cd $git_root_path
echo "These files needs new OSB config:"
while read -r line
do
modfied="$line"
echo "File for compare: $modfied"
if grep -qf $list_of_files" $modfied"; then
echo "Found: $modfied"
fi
done < <(git status -s | grep -v " M" | awk '{if ($1 == "M") print $2}')
$modified - is a string variable that stores path to file
Pattern file example:
SVCS/resources/
SVCS/bus/projects/busCallout/
SVCS/bus/projects/busconverter/
SVCS/bus/projects/Resources/ (ignore .jar)
SVCS/bus/projects/Teema/
SVCS/common/
SVCS/domain/
SVCS/techutil/src/
SVCS/tech/mds/src/java/fi/vr/h/service/tech/mds/exception/
SVCS/tech/mds/src/java/fi/vr/h/service/tech/mds/interfaces/
SVCS/app/cashmgmt/src/java/fi/vr/h/service/app/cashmgmt/exception/
SVCS/app/cashmgmt/src/java/fi/vr/h/service/app/cashmgmt/interfaces/
SVCS/app/customer/src/java/fi/vr/h/service/app/customer/exception/
SVCS/app/customer/src/java/fi/vr/h/service/app/customer/interfaces/
SVCS/app/etravel/src/java/fi/vr/h/service/app/etravel/exception/
SVCS/app/etravel/src/java/fi/vr/h/service/app/etravel/interfaces/
SVCS/app/hermes/src/java/fi/vr/h/service/app/hermes/exception/
SVCS/app/hermes/src/java/fi/vr/h/service/app/hermes/interfaces/
SVCS/app/journey/src/java/fi/vr/h/service/app/journey/exception/
SVCS/app/journey/src/java/fi/vr/h/service/app/journey/interfaces/
SVCS/app/offline/src/java/fi/vr/h/service/app/offline/exception/
SVCS/app/offline/src/java/fi/vr/h/service/app/offline/interfaces/
SVCS/app/order/src/java/fi/vr/h/service/app/order/exception/
SVCS/app/order/src/java/fi/vr/h/service/app/order/interfaces/
SVCS/app/payment/src/java/fi/vr/h/service/app/payment/exception/
SVCS/app/payment/src/java/fi/vr/h/service/app/payment/interfaces/
SVCS/app/price/src/java/fi/vr/h/service/app/price/exception/
SVCS/app/price/src/java/fi/vr/h/service/app/price/interfaces/
SVCS/app/product/src/java/fi/vr/h/service/app/product/exception/
SVCS/app/product/src/java/fi/vr/h/service/app/product/interfaces/
SVCS/app/railcar/src/java/fi/vr/h/service/app/railcar/exception/
SVCS/app/railcar/src/java/fi/vr/h/service/app/railcar/interfaces/
SVCS/app/reservation/src/java/fi/vr/h/service/app/reservation/exception/
SVCS/app/reservation/src/java/fi/vr/h/service/app/reservation/interfaces/
kraken_test.txt
namaker_test.txt
shmaker_test.txt
I need to compare file search pattern with a string, is it possible using grep?
I'm not sure I understand the overall logic, but a few immediate suggestions come to mind.
You can avoid grep | awk in the vast majority of cases.
A while loop with a grep on a line at a time inside the loop is an antipattern. You probably just want to run one grep on the whole input.
Your question would still benefit from an explanation of what you are actually trying to accomplish.
cd "$(git rev-parse --show-toplevel)"
git status -s | awk '!/ M/ && $1 == "M" { print $2 }' |
grep -Fxf .git/ForGeneratingSBConfigAlert.txt
I was trying to think of a way to add back your human-readable babble, but on second thought, this program is probably better without it.
The -x option to grep might be wrong, depending on what you are really hoping to accomplish.
This should work:
git status -s | grep -v " M" | awk '{if ($1 == "M") print $2}' | \
grep --file=.git/ForGeneratingSBConfigAlert.txt --fixed-strings --line-regexp
Piping the awk output directly to grep avoids the while loop entirely. In most cases you'll find you don't really need to print debug messages and the like in it.
--file takes a file with one pattern to match per line.
--fixed-strings avoids treating any characters in the patterns as special.
--line-regexp anchors the patterns so that they only match if a full line of input matches one of the patterns.
All that said, could you clarify what you are actually trying to accomplish?

Bash Output different from command line

I have tried all kinds of filters using grep to try and solve this but just cannot crack it.
cpumem="$(ps aux | grep -v 'grep' | grep 'firefox-bin' | awk '{printf $3 "\t" $4}'
I am extracting the CPU and Memory usage for a process and when I run it from the command line, I get the 2 fields outputted correctly:
ps aux | grep -v 'grep' | grep 'firefox-bin' | awk '{printf $3 "\t" $4}'
> 1.1 4.4
but the same command executed from within the bash script produces this:
cpumem="$(ps aux | grep -v 'grep' | grep 'firefox-bin' | awk '{printf $3 "\t" $4}')"
echo -e cpumem
> 1.1 4.40.0 0.10.0 0.0
I am guessing that it is picking up 3 records, but I just don't know where from.
I am filtering out any other grep processes by using grep -v 'grep', can someone offer any suggestions or a more reliable way ??
Maybe you have 3 records because 3 firefox are running (or one is running, and it is threading itself).
You can avoid the grep hazzle by giving ps and option to select the processes. E.g. the -C to select processes by name. With ps -C firefox-bin you get only the firefox processes. But this does not help at all, when there is more than one process.
(You can also use the ps option to output only the columns you want, so your line would be like
ps -C less --no-headers -o %cpu,%mem
).
For the triple-record you must come up with a solution, what should happen, where more than one is running. In a multiuser environment with programms that are threading there can always be situations where you have more than one process of a kind. There are many possible solution where none can help you, as you dont say, way you are going to do with it. One can think of solutions like selecting only from one user, and only the one with the lowest pid, or the process-leader in case of groups, to change the enclosing bash-script to use a loop to handle the multiple values or make it working somehow different when ps returns multiple results.
I was not able to reproduce the problem, but to help you debug, try print $11 in your awk command, that will tell you what process it is talking about
cpumem="$(ps aux | grep -v 'grep' | grep 'firefox-bin' | awk '{printf $3 "\t" $4 "\t" $11 "\n"}')"
echo -e cpumem
It's actually an easy fix for the output display; In your echo statement, wrap the variable in double-quotes:
echo -e "$cpumem"
Without using double-quotes, newlines are not preserved by converting them to single-spaces (or empty values). With quotes, the original text of the variable is preserved when outputted.
If your output contains multiple processes (i.e. - multiple lines), that means your grep actually matched multiple lines. There's a chance a child-process is running for firefox-bin, maybe a plugin/container? With ps aux, the 11th column will tell you what the actual process is, so you can update your awk to be the following (for debugging):
awk '{printf $3 "\t" $4 "\t" $11}'

How to get process id from process name?

I'm trying to create a shell script getting the process id of the Skype app on my Mac.
ps -clx | grep 'Skype' | awk '{print $2}' | head -1
The above is working fine, but there are two problems:
1)
The grep command would get all process if their name just contains "Skype". How can I ensure that it only get the result, if the process name is exactly Skype?
2)
I would like to make a shell script from this, which can be used from the terminal but the process name should be an argument of this script:
#!/bin/sh
ps -clx | grep '$1' | awk '{print $2}' | head -1
This isn't returning anything. I think this is because the $2 in the awk is treated as an argument too. How can I solve this?
Your ps -cl1 output looks like this:
UID PID PPID F CPU PRI NI SZ RSS WCHAN S ADDR TTY TIME CMD
501 185 172 104 0 31 0 2453272 1728 - S ffffff80145c5ec0 ?? 0:00.00 httpd
501 303 1 80004004 0 31 0 2456440 1656 - Ss ffffff8015131300 ?? 0:11.78 launchd
501 307 303 4004 0 33 0 2453456 7640 - S ffffff8015130a80 ?? 0:46.17 distnoted
501 323 303 40004004 0 33 0 2480640 9156 - S ffffff80145c4dc0 ?? 0:03.29 UserEventAgent
Thus, the last entry in each line is your command. That means you can use the full power of regular expressions to help you.
The $ in a regular expression means the end of the string, thus, you could use $ to specify that not only does the output must have Skype in it, it must end with Skype. This means if you have a command called Skype Controller, you won't pull it up:
ps -clx | grep 'Skype$' | awk '{print $2}' | head -1
You can also simplify things by using the ps -o format to just pull up the columns you want:
ps -eo pid,comm | grep 'Skype$' | awk '{print $1}' | head -1
And, you can eliminate head by simply using awk's ability to select your line for you. In awk, NR is your record number. Thus you could do this:
ps -eo pid,comm | grep 'Skype$' | awk 'NR == 1 {print $1}'
Heck, now that I think of it, we could eliminate the grep too:
ps -eo pid,comm | awk '/Skype$/ {print $1; exit}'
This is using awk's ability to use regular expressions. If the line contains the regular expression, 'Skype$', it will print the first column, then exit
The only problem is that if you had a command Foo Skype, this will also pick it up. To eliminate that, you'll have to do a bit more fancy footwork:
ps -eo pid,comm | while read pid command
do
if [[ "$command" = "Skype" ]]
then
echo $pid
break
fi
done
The while read is reading two variables. The trick is that read uses white space to divide the variables it reads in. However, since there are only two variables, the last one will contain the rest of the entire line. Thus if the command is Skype Controller, the entire command will be put into $command even though there's a space in it.
Now, we don't have to use a regular expression. We can compare the command with an equality.
This is longer to type in, but you're actually using fewer commands and less piping. Remember awk is looping through each line. All you're doing here is making it more explicit. In the end, this is actually much more efficient that what you originally had.
If pgrep is available on Mac, you can use pgrep '^Skype$'. This will list the process id of all processes called Skype.
You used the wrong quotes in your script:
ps -clx | grep "$1" | awk '{print $2}' | head -1
or
pgrep "^$1$"
The problem with your second example is that the $1 is in single quotes, which prevents bash from expanding the variable. There is already a utility that accomplishes what you want without manually parsing ps output.
pgrep "$1"
You can do this in AppleScript:
tell application "System Events"
set skypeProcess to the process "Skype"
set pid to the unix id of skypeProcess
pid
end tell
which means you can use 'osascript' to get the PID from within a shell script:
$ osascript -e "tell application \"System Events\"" -e "set skypeProcess to the process \"Skype\"" -e "set pid to the unix id of skypeProcess" -e "pid" -e "end tell"
3873
You can format the output of ps using the -o [field],... and list by process name using -C [command_name] ;however, ps will still print the column header, which can be removed by piping it through grep -v PID
ps -o pid -C "$1" |grep -v PID
where $1 would be the command name (in this case Skype)
I'd so something like:
ps aux | grep Skype | awk 'NR==1 {print $2}'
==== UPDATE ====
Use the parameter without quotes and use single quotes for awk
#!/bin/bash
ps aux | grep $1 | awk 'NR==1 {print $2}'
Method 1 - Use awk
I don't see any reason to use the -l flag (long format), I also don't see any reason to use grep and awk at the same time: awk has grep capability built in. Here is my plan: use ps and output just 2 columns: pid and command, then use awk to pick out what you want:
ps -cx -o pid,command | awk '$2 == "Skype" { print $1 }'
Method 2 - Use bash
This method has the advantage that if you already script in bash, you don't even need awk, which save one process. The solution is longer than the other method, but very straight forward.
#!/bin/bash
ps -cx -o pid,command | {
while read pid command
do
if [ "_$command" = "_$1" ]
then
# Do something with the pid
echo Found: pid=$pid, command=$command
break
fi
done
}
pgrep myAwesomeAppName
This works great under Catalina 10.15.2
Use double quotes to allow bash to perform variable substitution.
Single quotes disable bash variable substitution mechanism.
ps -clx | grep "$1" | awk "{print $2}" | head -1

Resources