1

I have a command (logcat) which outputs a long log like this:

07-27 09:26:22.416 17574 17886 D SurfaceControl: Excessive delay in setPowerMode()
07-27 09:26:22.421 17574 17599 I PowerManagerService: Sleeping (uid 1000)...
07-27 09:26:22.427 17489 17527 D audio_hw_primary: adev_set_parameters: enter: screen_state=off

The format is: [Date] [Time] [Process ID] [Thread ID] [Log Type] [Text]

Instead of the process id, I would like to see the name of the process (or package to be more accurate).

The name of a package can be retrieved by process ID using:

echo "$PKG_PID_LIST" | grep -w $PID | cut -d ' ' -f1

E.g.:

$ echo "$PKG_PID_LIST" | grep -w 17489 | cut -d ' ' -f1
org.lineageos.audiofx

If you're curious, yes, I'm on Android and I'm creating the list using:

PKG_PID_LIST=$(eval $(pm list packages | cut -d ':' -f 2 | sed 's/^\(.*\)$/echo \"\$\(echo \1\) \$\(pidof \1\)\";/'))

In the end the output should look something like this:

07-27 09:26:22.416 com.android.systemui 17886 D SurfaceControl: Excessive delay in setPowerMode()
07-27 09:26:22.421 com.android.systemui 17599 I PowerManagerService: Sleeping (uid 1000)...
07-27 09:26:22.427 org.lineageos.audiofx 17527 D audio_hw_primary: adev_set_parameters: enter: screen_state=off

edit: Example output of $PKG_PID_LIST:

$ echo "$PKG_PID_LIST"
com.android.deskclock 18937
com.android.systemui 17574
net.osmand.srtmPlugin.paid 
com.android.bluetoothmidiservice 
org.lineageos.audiofx 17489

(Packages that are not running don't have a PID. There is always a space behind the package name.)

2

Reading from multiple sources of input and applying custom actions calls for the need for awk. You could simply do that as

awk 'FNR==NR{pidMap[$2]=$1; next}$3 in pidMap{$3=pidMap[$3]}1' <(echo "$PKG_PID_LIST") <(logcat)

The <() is a syntax provided by the bash shell that runs the command inside and makes the output of the command as if it were appearing in a file. As the 2nd file content we make the output of logcat appear there. So awk tries to read from two input streams.

The logic FNR==NR in awk used when we reading from more than one input stream at a time. FNR and NR are special variables that keep track of the line numbers per file and throughout the entire input stream. So with the condition FNR==NR we basically define the action after it within {..} to run on the first file and the one after it on the second.

So on the first file-like stream, we see the the output of PKG_PID_LIST from which we create a hash-map with key as the process-id and the process name as the value. Once we are done processing this file, we would have mapped all the PIDs with their names.

On the second file, we use this map $3 in pidMap meaning, the value of $3 in the 2nd stream is present as the key we just processed, we update the $3 on the second stream ($3 - third space delimited column) as the mapped value for the key. The {..}1 is an action in awk where we instruct it to reconstruct the line based on the modifications done (if no modifications done, print line as such).


If your logcat is producing output continuously and you want to run this command after the output is settled, then I suggest writing the output to a temporary file first and then pass that file as the 2nd argument in awk.

0

You could do it as follows:

logcat |\
_PKG4PID_="$PKG_PID_LIST" \
perl -lpe '
    %h = reverse split /\s+/, $ENV{_PKG4PID_} if $. == 1;
    s/(?:\H+\h+){2}\K(\H+)/$h{$1}/;
' 

Explanation:

  • set the environment variable _PKG4PID_ equal to your shell variable PKG_PID_LIST.
  • For the first line of the input being read by perl, initialize the hash %h with the keys as the 2nd fields and values as 1st fields.
  • Now it is just a matter of locating the 3rd field in the current line, /(?:\H+\h+){2}\K(\H+)/, and replacing it with the corresponding hash entry, $h{$1}.

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.