3

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)
  PARGS_ARR=($PARGS)
  NODE='UNKNOWN'
  for ARG in ${PARGS_ARR[@]}
  do
    # 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
      NODE=$ARGR
    fi
  done

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?

1

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'
1234

So your script could become:

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

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=' '{
          for(i=2;i<=NF;i++){
              sub(" .*","",$i);
              print $i
          }
        }'
))
  • Returns: awk: syntax error near line 1 awk: illegal statement near line 1 Can't rigure out why. – user1065145 Mar 21 '14 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 '14 at 12:46
  • That worked, but with nawk! THANKS A LOT!!! – user1065145 Mar 21 '14 at 13:17
  • @user1065145 This is strange. As far as I know, this solution uses only POSIX constructs. – Joseph R. Mar 21 '14 at 13:57
  • @JosephR. But... POSIX arrays? – mikeserv Mar 21 '14 at 18:07
1

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)
PIDSED
1

Process the arguments using bash, not external processes

for ARG in "${PARGS_ARR[@]}"
do
  # trim single quotes 
  ARG=${ARG//\'}

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

  if [[ "$ARGL" == "-DnodeId" ]]; then
    NODE=$ARGR
  fi
done

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.