Подстановка команд в выражении sed - PullRequest
1 голос
/ 05 октября 2011

У меня небольшая проблема с bash / sed. Мне нужно иметь возможность использовать подстановку команд в выражении sed. У меня есть два больших текстовых файла:

  • первым является logfile.txt, который иногда * показывает сообщения об ошибках по идентификатору (0xdeadbeef - типичный пример) в формате ERRORID: 0xdeadbeef

  • second errors.txt содержит сообщения об ошибках, хранящиеся в парах

Я пытался использовать sed с подстановкой команд bash для выполнения задачи:

cat logfile.txt | sed "s/ERRORID:\(0x[0-9a-f]*\)/ERROR:$(cat errors.txt |
    grep \1 | grep -o '^[A-Z_]*' )/g"

(^^^ это должно быть в одной строке, конечно)

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

   Lot's of meaningless stuff ERRORID:0xdeadbeef and something else =>
=> Lot's of meaningless stuff ERROR:LONG_ERROR_DESCRIPTION and something else 

Но это не так. Проблема в том, что sed не может «вставить» секцию регулярных выражений (\ 1) в подстановку команд. Какие у меня есть другие варианты? Я знаю, что сначала можно создать sed-выражение или сделать это другим способом, но я бы не хотел разбирать эти файлы несколько раз (они могут быть огромными).

Как всегда большое спасибо за любую помощь.

* в журнале нет реального форматирования. Разделы, столбцы, табуляция / разделение запятой используются непоследовательно

PS. Просто чтобы объяснить. Следующее выражение работает, но, конечно, в нем нет аргументов, передаваемых:

echo "my cute cat" | sed "s/cat/$(echo dog)/g"

Ответы [ 4 ]

2 голосов
/ 05 октября 2011

Вы можете создать сценарий sed из каталога сообщений об ошибках, а затем применить этот сценарий sed к файлу журнала.

В основном, что-то вроде этого:

sed 's/\(.*\), 0x\([0-9A-F]*\)$/s%ERRORID:0x\2%ERROR:\1%g/' errors.txt |
sed -f - logfile.txt

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

s%ERRORID:0x00000001%ERROR:Out of memory%
s%ERRORID:0x00000002%ERROR:Stack overflow%
s%ERRORID:0x00000031%ERROR:values of beta may cause dom%

То есть новый скрипт sed, в котором указывается замена для каждого кода ошибки в каталоге.

Существуют разные диалекты sed, поэтому для этого может потребоваться небольшая настройка. Я полагаю, что sed в Linux должен использовать обратную косую черту перед группировкой скобок в регулярных выражениях и с радостью допускать стандартный ввод в качестве аргумента опции -f. Это не переносится на другие Unices (но вы можете заменить Perl на sed, если вам нужна переносимость).

* Редактировать: Если сообщения об ошибках довольно статичны и / или вы хотите прочитать журнал со стандартного ввода, сохраните сгенерированный скрипт в файл;

# Do this once
sed 's/\(.*\), 0x\([0-9A-F]*\)$/s%ERRORID:0x\2%ERROR:\1%g/' errors.txt >errors.sed
# Use it many times
sed -f errors.sed logfile.txt

Вы также можете добавить #!/usr/bin/sed -f вверху errors.sed и chmod +x, чтобы превратить его в автономный командный скрипт.

1 голос
/ 19 июля 2018

С GNU awk для gensub () и аргументом 3rg для match ():

$ awk '
    NR==FNR {
        map[$NF] = gensub(/,[^,]+$/,"",1)
        next
    }
    match($0,/(.*ERRORID:)(0x[[:xdigit:]]+)(.*)/,a) {
        $0 = a[1] (a[2] in map ? map[a[2]] : a[2]) a[3]
    }
1' errors.txt logfile.txt
Lot's of meaningless stuff ERRORID:LONG_ERROR_DESCRIPTION and something else =>

Вышеприведенное будет работать намного быстрее, чем сценарии sed в текущем принятом ответе, и не будет давать сбой, учитывая различныевозможное содержимое LONG_ERROR_DESCRIPTION, такое как % или & или \1, и оно не будет работать, если данный ERRORID является подмножеством другого, например, если 0xdead и 0xdeadbeef являются 2 отдельными кодами ошибок, тогдаСценарии sed могут завершаться с ошибкой в ​​зависимости от порядка их появления в errors.txt, например, они могут преобразовывать ERRORS:0xdeadbeef в ERRORS:LONG_ERROR_DESCRIPTIONbeef.сначала сопоставив 0xdead.

1 голос
/ 05 октября 2011

Я не знаю, сработает ли это, поскольку я не могу получить ответ о том, сохраняются или нет группы захвата, но гораздо больше, чем просто команда s, Я думал, что вы можете использовать группу захвата в селекторе строк регулярных выражений, а затем использовать это для подстановки команд. Примерно так:

/ERRORID:\(0x[0-9a-f]*\)/  s/ERRORID:0x[0-9a-f]*/ERROR:$(grep \1 errors.txt | grep -o '^[A-Z_]*' )/

В любом случае, если это не сработает, я бы поменял передачу и указал, что это действительно хорошая работа для Perl. Вот как я это сделаю, что, на мой взгляд, намного чище / проще для понимания:

#!/usr/bin/perl

while(<>) {
  while( /ERRORID:(0x[0-9a-f]*)/ ) {
    $name = system("grep $1 errors.txt | grep -o '^[A-Z_]*'");
    s/ERRORID:$1/ERROR:$name/g;
  }
  print;
}

Затем выполните:

./thatScript.pl logfile.txt
0 голосов
/ 06 октября 2011

Просто чтобы люди искали решение с голой оболочкой и седом. Не идеально, но работает:

cat logfile.txt | while read line ; do id=$(echo -E "$line" | 
    grep "ERRORID:0x[0-9a-f]*" | grep -o "0x[0-9a-f]*" ) ; 
    if [ ! -z "$id" ] ; then echo -E "$line" | sed "s/$id/$(grep $id errors.txt | 
    grep -o '^[A-Z_]*' )/g" ;else echo -E "$line" ; fi ; done

Если вы видите некоторые варианты исправления, пожалуйста, поделитесь.

...