egrep gives me what I want, grep -E does not - shell

I have read that egrep is deprecated in favour of grep -E so I'm looking back at my code and changing it where I find it.
But I see that the results are not always as expected.
With egrep, I get my list of running Oracle instances:
ps -ef|grep [p]mon|awk -F'ora_pmon_' '{print $NF}'|egrep -v 'ASM|^$'
halana
bila
halsasa
loana
However with grep -E, I get part of the awk in the results:
ps -ef|grep [p]mon|awk -F'ora_pmon_' '{print $NF}'|grep -Ev 'ASM|^$'
halana
bila
halsasa
{print $NF}
loana
Excluding 'print' obviously works but is it the right way to go about it?:
ps -ef|grep [p]mon|awk -F'ora_pmon_' '{print $NF}'|grep -Ev 'ASM\|^$|print'
halana
bila
halsasa
loana
Is this effect due to the fact that grep -E allows additional regular expression syntax?

Suggesting to simplify your line, eliminate all grep commands:
pgrep -af 'pmon'|awk -F'ora_pmon_' '!/ASM|^$/{print $NF}'
Fold first grep command into pgrep command.
Fold second grep command into awk scirpt !/ASM|^$/{print $NF}

About grep -E vs egrep
There is no difference whatsoever between these commands (they're even provided by the same executable on most operating systems); your old code was vulnerable to this bug too, and if you didn't see it, that's just a matter of being "lucky" in ps getting enough of the process list read before the shell gets to setting up the awk part of the pipeline.
Solving Your Problem
The right answer is already given by #DudiBoy, but the answer with the smallest possible change would be to make your awk (assuming it has GNU extensions and accepts regexes in the field separator specification) escape the string pmon the same way your first grep command does:
ps -ef|grep '[p]mon'|awk -F'ora_[p]mon_' '{print $NF}'|grep -Ev 'ASM|^$'

Related

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;}'

Trying to kill processes in bash - code embedded in [...] not run?

here is what I want to do
while ["ps a | grep '[m]ono' | awk '{print $1}'" != ""] ; do
kill ps a | grep '[m]ono' | awk '{print $1}'
done
meaning if the grep returns nothing, don't try a kill.
The thing is I've always been lost with the expression evaluation in bash, sometime I use " " around something, sometime it's eval, sometime it's ''.
Could someone explain to me how to write the condition in my loop and explain the difference between the above? I'm used to find the working one with many tries and it feels like a huge loss of time.
Best choice: Do something else.
Utilities already exist for this purpose. Example:
killall mono
pkill mono
...or, even better, something targeted to the specific executable you want to terminate:
fuser -k /path/to/something.exe
...which would kill only programs with a file handle on that specific executable, rather than all applications running with mono on the machine.
...but, to explain the bugs:
There are two things wrong here: Missing command substitutions, and missing whitespace.
Missing whitespace:
["ps a | grep '[m]ono' | awk '{print $1}'" != ""]
...is literally trying to run a command with a name starting with [ps, as in, looking in the PATH for...
/bin/[ps\ a
/usr/bin/[ps\ a
...etc. [ is a command, and needs a space after its name like any other command. Thus:
[ "ps a | grep '[m]ono' | awk '{print $1}'" != "" ]
...fixes this problem (but leaves another one).
Missing command substitutions:
[ "ps a | grep '[m]ono' | awk '{print $1}'" != "" ]
...is comparing a string that starts with "ps a" to to ""; it does not compare the output of running a command that starts with ps a. To do that, you'd instead run:
[ "$(ps a | grep '[m]ono' | awk '{print $1}')" != "" ]
The content of $(...) is replaced with the output of the command within; thus, running your pipeline and comparing its output to an empty string.

Complicated grep commands not executing in shell script

I am trying to execute a couple of complicated grep commands via a shells script that work fin in the terminal manually executed. I can't for the life of me figure out why this doesn't work.
The goal of the first grep is to get out any process id attached to the parent myPattern. The 2nd get the process id of the process myPattern
Currently my shell script
returns nothing for the 1st.
ignores the "grep -v 'grep'" part in the 2nd.
#!/bin/sh
ps -ef | grep "$(ps -ef | grep 'myPattern' | grep -v grep | awk '{print $2}')" | grep -v grep | grep -v myPattern | awk '{print $2}'
ps -ef | grep 'myPattern' | grep -v 'grep' | awk '{print $2}'
This works fine when run in the terminal manually. Any ideas where i have stuffed this up?
Your first command is vague. I don't think it would reliably do what you describe. It also does not guard against getting the id of the first grep call. The second one works for me. For the fists query it highly depends on the system you are using. It's easier to use pstree to show you the whole process tree under a pid. Like:
pstree -p 1782 | sed 's/-/\n/g' | sed -n -e 's/.*(\([0-9]\+\)).*/\1/p'
You need to limit pid to be a single value. If you have more values, then you have to loop through them. If you don't have pstree, then you can craft some loop around ps. Make note that even if you current commands worked, then thwy would catch only one level parent/child relationships. pstree does any level.
I also have to tell you that a process can escape original parent as a parent process by forking.
In any case without exact details what you are trying to achieve and why, and on what platform it is hard to give you a great answer. Also these utilities albeit present virtually everywhere are not as portable as one would wish.
One more note - /bin/sh is often not your current shell. On many linux systems user has a default shell of bash and /bin/sh is dash or some other shell variant. So if you see diffs with what you have in console and script, it can be difference in actual shell you are using.
Based on user feedback it would be much easier to have something like this in the java process launching script:
java <your params here> &
echo $! > /var/run/myprog.pid
Then the kill script would look like echo /var/run/myprog.pid | xargs kill. There are shorter commands but I think this is more portable. Give actual code if you want more specific.

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 do you pipe input through grep to another utility?

I am using 'tail -f' to follow a log file as it's updated; next I pipe the output of that to grep to show only the lines containing a search term ("org.springframework" in this case); finally I'd like to make is piping the output from grep to a third command, 'cut':
tail -f logfile | grep org.springframework | cut -c 25-
The cut command would remove the first 25 characters of each line for me if it could get the input from grep! (It works as expected if I eliminate 'grep' from the chain.)
I'm using cygwin with bash.
Actual results: When I add the second pipe to connect to the 'cut' command, the result is that it hangs, as if it's waiting for input (in case you were wondering).
Assuming GNU grep, add --line-buffered to your command line, eg.
tail -f logfile | grep --line-buffered org.springframework | cut -c 25-
Edit:
I see grep buffering isn't the only problem here, as cut doesn't allow linewise buffering.
you might want to try replacing it with something you can control, such as sed:
tail -f logfile | sed -u -n -e '/org\.springframework/ s/\(.\{0,25\}\).*$/\1/p'
or awk
tail -f logfile | awk '/org\.springframework/ {print substr($0, 0, 25);fflush("")}'
On my system, about 8K was buffered before I got any output. This sequence worked to follow the file immediately:
tail -f logfile | while read line ; do echo "$line"| grep 'org.springframework'|cut -c 25- ; done
What you have should work fine -- that's the whole idea of pipelines. The only problem I see is that, in the version of cut I have (GNU coreutiles 6.10), you should use the syntax cut -c 25- (i.e. use a minus sign instead of a plus sign) to remove the first 24 characters.
You're also searching for different patterns in your two examples, in case that's relevant.

Resources