Bash заменить строку с командой

У меня есть команда (logcat), которая выводит длинный журнал, как это:

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 

Формат: [Date] [Time] [Process ID] [Thread ID] [Log Type] [Text]

Вместо идентификатора процесса я хотел бы видеть имя процесса (или пакет, чтобы быть более точным).

Имя пакета можно получить по идентификатору процесса, используя:

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

Например:

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

Если вам интересно, да, я нахожусь на Android и создаю список, используя:

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

В итоге вывод должен выглядеть примерно так:

 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: пример вывода $ 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 

(Пакеты, которые не работают, не имеют PID. За именем пакета всегда есть пробел.)

Чтение из нескольких источников ввода и применение пользовательских действий требует использования awk . Вы можете просто сделать это как

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

<() - это синтаксис, предоставляемый оболочкой bash которая запускает команду внутри и выводит команду, как если бы она появлялась в файле. В качестве 2-го содержимого файла мы делаем вывод logcat там. Таким образом, awk пытается читать из двух входных streamов.

Логика FNR==NR в awk используется, когда мы читаем из более чем одного входного streamа одновременно. FNR и NR - это специальные переменные, которые отслеживают номера строк в файле и во всем входном streamе. Таким образом, с условием FNR==NR мы в основном определяем действие после него в пределах {..} для запуска в первом файле и после него во втором.

Таким образом, в первом streamе, подобном файлу , мы видим вывод PKG_PID_LIST из которого мы создаем hash-карту с ключом в качестве идентификатора процесса и именем процесса в качестве значения. Как только мы закончим обработку этого файла, мы сопоставим все PID с их именами.

Во втором файле мы используем эту карту $3 in pidMap значении $3 in pidMap , значение $3 во 2-м streamе присутствует в качестве ключа, который мы только что обработали, мы обновляем $3 во втором streamе ( $3 - столбец с разделителем третьим пробелом) в качестве отображенного значение для ключа. {..}1 - это действие в awk где мы инструктируем его восстановить линию на основе выполненных изменений (если никаких изменений не сделано, выведите строку как таковую).


Если ваш logcat выходные данные непрерывно, и вы хотите выполнить эту команду после того, как выходные данные установлены, то я предлагаю сначала записать выходные данные во временный файл, а затем передать этот файл как второй аргумент в awk .

Вы можете сделать это следующим образом:

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

Объяснение:

  • установите переменную среды _PKG4PID_ равной переменной оболочки PKG_PID_LIST .
  • Для первой строки ввода, читаемой perl , инициализируйте хеш %h с ключами в качестве 2-х полей и значениями в качестве 1-х полей.
  • Теперь нужно просто найти 3-е поле в текущей строке, /(?:\H+\h+){2}\K(\H+)/ , и заменить его на соответствующую запись хеша, $h{$1} ,