I get list of PIDs in my bash scripts (Java processes) and have to analyze their command-line arguments to determine which instance of server each PID corresponds to.

At the moment I do it that way using sed/cut:

  PARGS=$(pargs -l $PID)
  for ARG in ${PARGS_ARR[@]}
    # trim single quotes 
    ARG=$(echo $ARG | sed "s/'//g")

    # split by equals sign
    ARGL=$(echo $ARG | cut -f1 -d=)
    ARGR=$(echo $ARG | cut -f2 -d=)

    if [ "$ARGL" == "-DnodeId" ]; then

But it works EXTREMELLY SLOW due to large number of command-line parameters (about 20-30 per each PID).

Is there a way to somehow parse command-line parameters and get key=>value parse with a single command?


4 Answers 4


What does the output of pargs -l $PID look like? From your code, it seems like it is a single line containing all the command-line arguments in a format eg:

arg1=val1 arg2=val2

If so, you can collect the value for the -DnodeId argument with a sed command:

$ ARGS="-DfirstArg=foo -DanotherArg=bar -DnodeId=1234 -DlastArg=baz"
$ echo "$ARGS" | sed -r 's/.*-DnodeId=([^ ]+).*/\1/g'

So your script could become:

PARGS=$(pargs -l $PID)
if [ -n "$(grep "DnodeId" <(echo "$PARGS"))" ]; then
    NODE=$(echo "$PARGS" | sed -r 's/.*-DnodeId=([^ ]+).*/\1/g')

If all you care about is what comes to the right of = if what's on the left is -DnodeId, you can do this:

NODE=$(pargs -l $PID| awk -F '-DnodeId=' '{sub(" .*","",$2);print $2}')

This will print whatever comes to the right of the pattern -DnodeId= up to but excluding the first space. If there's more than one -DnodeId on the command line, it will work with the first one only.

To handle more than one -DnodeId per line is also possible:

NODES=($(pargs -l $PID| awk -F '-DnodeId=' '{
              sub(" .*","",$i);
              print $i
  • Returns: awk: syntax error near line 1 awk: illegal statement near line 1 Can't rigure out why. Mar 21, 2014 at 12:42
  • @user1065145 What OS is this? This works fine on my (g)awk 4.0.1 running on Debian.
    – Joseph R.
    Mar 21, 2014 at 12:46
  • That worked, but with nawk! THANKS A LOT!!! Mar 21, 2014 at 13:17
  • @user1065145 This is strange. As far as I know, this solution uses only POSIX constructs.
    – Joseph R.
    Mar 21, 2014 at 13:57
  • @JosephR. But... POSIX arrays?
    – mikeserv
    Mar 21, 2014 at 18:07

It seems that what you're doing would definitely be very slow as every time you var=$(command substitute) you must stop and wait for its output before you move to the next step. I'm willing to bet you'd wait a lot less if you just processed it in stream with sed, the stream editor:

NODE="$(pargs -l $PID | sed -rn '/(-DnodeId)=(\S*)/{s//\2/pq}')"

I'm not totally sure about the single quotes - I just typed this on my phone - but sed's y function can certainly handle it if you still need it.

Above Josh demonstrates a fail case. Probably it would be much simpler just to add:

${NODE:?PID not found...quitting}

After running the above sed command.

It occurs to me these are not necessarily already separated by line, we can handle that easily enough with 1 more |pipe - still all in one data stream, while also accounting for the possibility of multiple matches:

. <<PIDSED /dev/stdin
    $(pargs -l $PID |\
        sed -rn 's/(-DnodeId)=(\S*)/\
            echo "NODE$((i=i+1))=\2" ;/gp' |\
        . /dev/stdin)

Process the arguments using bash, not external processes

for ARG in "${PARGS_ARR[@]}"
  # trim single quotes 

  # split by equals sign
  IFS="=" read ARGL ARGR <<< "$ARG"

  if [[ "$ARGL" == "-DnodeId" ]]; then

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .