How can grep interpret literally a string that contains an asterisk and is fed to grep through a variable? - bash

I have this script:
#!/bin/bash
CLASSPATH="/blah/libs/*:/blah/more/libs"
CMD="java -cp $CLASSPATH MainClass"
ALREADY_RUNNING_PID=`ps -ef --no-headers | grep $CMD | grep -v grep | awk '{print $2}'`
if [ "$ALREADY_RUNNING_PID" ]; then
echo "Already running"
exit 1
fi
$CMD &
problem is it doesnt work due to the asterisk in the CMD variable. how can i tell grep to see the variable value as it is? Any solution? It is mandatory that grep is fed through the variable.
Thanks.

Since you are not using regular expressions you can use fgrep $CMD instead of grep

The problem is not grep, it's
CLASSPATH="/blah/libs/*:/blah/more/libs"
If you do
echo $CLASSPATH
you should see that your shell has expanded the * to all files in that directory. To remedy this, just use single quotes to prevent globbing:
CLASSPATH='/blah/libs/*:/blah/more/libs'

Totally unrelated to your specific grep problem, but jps will report on running Java processes and possibly make your grepping easier since you'd most likely have to just do:
jps | grep MainClass
(or something similar)

Related

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

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|^$'

bash command substitution's result not consistent

I have a script.
cat /root/test/ddd.sh
#!/bin/bash
s=/root/test/ddd.sh
ps -ef | grep $s | grep -v grep
result=$(ps -ef | grep $s | grep -v grep | wc -l)
echo $result
when i execute it, the result is weird, it shows that there is two line matched.
[root#l2 test]# /root/test/ddd.sh
root 15361 15032 0 09:52 pts/18 00:00:00 /bin/bash /root/test/ddd.sh
2
That's because you're running a subshell. That is, the $(...) piece causes bash to fork, thereby creating two (nearly) identical processes. By identical, I mean basically everything except for process ID, parent process ID, return code from the fork, and I can't think of anything else. But one thing that does remain the same for sure is the command line. Both of them will be "/bin/bash /root/test/ddd.sh". So, inside the result=$(...), there will be exactly one extra process that matches.
You can see this, for example, by removing the | wc -l piece at the end of your $(...), and, to make it more readable, enclose the echo's argument in quotes:
result="$(ps -ef | grep $s | grep -v grep)"
echo "$result"
Here you will see that there are two bashes, and the PPID of one is the PID of the other, showing the parent-child relationship.

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.

"/bin/grep: Unmatched [ or [^" in bash loop

I am trying to write a Bash alias (or script) which lists which of some particular programs are currently running.
In my .bashrc I have set $MY_BIN_PATH to a directory containing all the programs of interest to me:
export MY_BIN_PATH=/root/repo/bin
$ > echo $MY_BIN_PATH
/root/repo/bin
Listing that directory shows a number of files. I want to grep the output of ps -ef for each of these:
for i in `ls $MY_BIN_PATH`;
do
ps -ef | grep $i | egrep -v "grep|md_m|avahi";
done
Regardless of which (if any) of the files in $MY_BIN_PATH are running, the output is always:
/bin/grep: Unmatched [ or [^
/bin/grep: Unmatched [ or [^
/bin/grep: Unmatched [ or [^
/bin/grep: Unmatched [ or [^
/bin/grep: Unmatched [ or [^
It seems like somehow the value of $i is "forgotten" in the pipe between ps -ef and grep $i.
What am I doing wrong here, and how can I make it work?
First, check if ls is aliased to something. Maybe you should use the absolute path to ls and without any options. Make sure it is not system-aliased to color-ls or something.
Second, you can change the loop header to
for i in $MY_BIN_PATH/*
and that may fix it as well.
As a start, never parse the output of ls
Secondly, parameters needs to be properly quoted
Try this:
for i in $MY_BIN_PATH/*
do
ps -ef | grep "$i" | egrep -v "grep|md_m|avahi"
done
To get the PID of a running process when you know the process name, use pidof().
What are you trying to achive with the loop in the end?

Shell scripting obtain command PID

In a shell script lets say i have run a command like this
for i in `ps -ax|grep "myproj"`
do
echo $i
done
Here, the grep command would be executed as a separate process. Then how do i get its PID in the shell script ?
I'm going out on a limb here, and understand this looks more like a comment.
Why do you need the PID of the grep command?
In your comment you say you want to compare it in the loop against something. I would suppose that it is your issue that that the loop will (sometimes) not only include myproj but also an item about your grep command? If so, try the following:
for i in `ps -ax | grep -v grep | grep "myproj"`
do
echo $i
done
The -v switch basically inverts the pattern, so grep -v grep (or grep -v "grep", which maybe looks a bit less awkward) will include only lines that do not include the string "grep" (see man grep).
Note that this maybe overly vague for some cases, for example if the pattern you actually look for also contains the string "grep". For example, the following might not work as you'd expect: ps -ax | grep -v grep | grep mygrepling
However, in your particular case, where you only look for "myproj" it will do.
Or you could simply use
for i in `ps -ax | grep "my[p]roj"`
do
echo $i
done
That way there is no need to know the PID of the grep command, because it simply never shows up as a loop iteration.
When you run a process in background, you can get its PID in $!
$ ps aux | grep dddddd & echo $!
[1] 27948
27948
ic 27948 0.0 0.0 3932 760 pts/3 R 08:49 0:00 grep dddddd
When in foreground --- the process does not exist anymore at the point you want to find its PID. When you are in the loop, the for statement is already executed and grep is already exited, so you can not find its PID anymore.

Resources