Help with xargs

Using the bash shell I'm trying to either create a command for the command line or a script that will show netstat info for a given process name. Here is an example of what I'm trying to do:

$ ps aux |grep catalina |grep -v grep | awk '{print $2}'
5132
$ netstat -nlp |grep 5132
(Not all processes could be identified, non-owned process info
 will not be shown, you would have to be root to see it all.)
tcp        0      0 0.0.0.0:8000            0.0.0.0:*               LISTEN      5132/java       
tcp6       0      0 :::8009                 :::*                    LISTEN      5132/java       
tcp6       0      0 :::8080                 :::*                    LISTEN      5132/java       
tcp6       0      0 127.0.0.1:8005          :::*                    LISTEN      5132/java       

I would like to be able to use xargs such that the output from the first command is used as input to grep in the second command. How would I put all this together to have one "simple" command to search for all the ports being listened to by a particular application?

If it were put in a script I would like to be able to pass in the name of the application I'm searching for so I could run the above like this:

$ show_netstat_for_app.sh catalina

Apparently catalina has its port as string 2 here, but most things won't. If you want something that works for catalina, great.

Here's a tip: Instead of doing 'grep -v grep', just make it so grep doesn't match itself in the first place by making one letter [c] instead of c -- matches the same string but doesn't match itself.

Also, whenever you're doing grep | grep | awk | kitchen | sink, you should just replace it all with one simple awk command.

#!/bin/sh

set -- `ps -aux | awk '/[c]atalina/ { print $2 }'` # $1=PID1, $2=PID2, etc
IFS="|" # Make $* be PID1|PID2|PID3 instead of PID1 PID2 PID3
netstat -nlp | egrep "($*)/" # egreps for "(PID1|PID2)/"
1 Like

I don't think this is specific to catalina. In my example, catalina was just an example of a program I was searching for. the output is actually coming from ps -aux. So, it looks like it would always be column 2 unless ps was configured to reorder the columns.

I'm also a newbie with awk. In my original post I mentioned that I would also like to have "catalina" be a parameter that I could pass into the script. If I wanted to do this search for some other application, say httpd for example, would there be an easy way to pass that in as a parameter to the script? I was thinking that using the form of

grep $1 |grep -v grep

would make it easier to replace the parameter, "catalina" in this example, with a parameter passed into the script rather than having to make the script wrap around the first character of $1.

What would the appropriate awk command be to search for the passed in parameter "$1" instead of searching for "/[c]atalina/" if I don't use the grep | grep -v form?

Also, is there a way to do this with xargs?

Oh, I thought you were somehow extracting port numbers from ps. PID's make more sense.

But if all you want is PID's, pgrep does everything in one shot without the grep -v grep problem. It's also more portable than ps aux AFAIK.

set -- `pgrep $1`
IFS="|" # Make $* be PID1|PID2|PID3 instead of PID1 PID2 PID3
netstat -nlp | egrep "($*)/" # egreps for "(PID1|PID2)/"

xargs isn't applicable here; it's not a shell, it doesn't run pipe chains. Running netstat 12 times to find 12 different PID's would be silly, anyway.

Cramming everything into one line doesn't necessarily make it simpler or more efficient.

What about this one - not yet bullet proof, may need to be adapted / improved:

$ { ps; netstat -nlp; } 2>/dev/null | awk 'match ($15, /[l]xterminal/) {TMP=$4} match ($9, TMP"/")'
unix  2      [ ACC ]     STREAM     LISTENING     73839    2512/lxterminal     /tmp/.lxterminal-socket:0-user

Thank you for your replies. I can see from this seemingly simple question that I have a lot to learn about shell scripting. I don't have a clue about what is happening for either of the solutions listed.

Will you explain to me what each of the commands are doing in the solutions, or refer me to a good book for shell scripting?

Thank you.

{ list; } is a group command, the components of list are executed one after the other, and all stdouts of the list are piped to awk , which first sees the ps output, checks $15 (may be different in your case) for your command name (I used "lxterminal" for testing) and retains $4 (PID) in TMP when found (not safe for multiple occurrences of cmd in ps!). Then, when the output of netstat is in the pipe, it looks for the PID in $9 and outputs each matching row.

1 Like

Try this:

set -- a b c
echo $1
echo $2
echo $3
echo "$*"
IFS="|"
echo "$*"

and you'll see what I'm doing.

That was really helpful for me to understand what is going on here. So, I see what's happening but I'm still not sure of the purpose of the IFS command is. I see what it is doing, setting the delimiter to |. But what purpose does that serve for the remainder of the script? Somehow it must have an effect when running the last line.

netstat -nlp | egrep "($*)/"

It's not a command, it's a special variable which determines what value(s) the shell splits and joins strings upon. Setting it to | allows me to join $1 $2 $3 ... together like "A|B|C" so I can grep for more than one thing at once -- that being the syntax egrep uses for multiple search strings. plain grep doesn't have that.

egrep also lets you group things with (). So It searches for (PID1|PID2|PID3)/, so PID1/, or PID2/, or PID3/. That way it won't accidentally match a number elsewhere in netstat, it will just look for them in the PID field where they belong.

1 Like

Nice! Thank you for your help. Here is my final solution. If you have any suggestions please let me know. I didn't end up using awk the way you suggested because I couldn't get it to work.

$ cat portsfor 
#!/bin/bash
set -- `ps aux | grep $1 | grep -v grep | grep -v $0 | awk '{print $2}'`
IFS="|"
sudo netstat -nlp | grep -E "($*)/"
$ ./portsfor catalina
[sudo] password for axiopisty: 
tcp        0      0 0.0.0.0:8000            0.0.0.0:*               LISTEN      11389/java      
tcp6       0      0 :::8009                 :::*                    LISTEN      11389/java      
tcp6       0      0 :::8080                 :::*                    LISTEN      11389/java      
tcp6       0      0 127.0.0.1:8005          :::*                    LISTEN      11389/java      
$ 

pgrep $1 ought to spit out PIDs (and only PIDs, no filtering needed) matching catalina without matching itself or the script. Use it directly, ps not needed.