sed表达式中的命令替换

时间:2011-10-05 18:49:36

标签: linux bash sed substitution

我对bash / sed没什么问题。我需要能够在sed表达式中使用命令替换。我有两个大文本文件:

  • 首先是logfile.txt,其中<​​strong>有时* 按ID显示错误消息(0xdeadbeef是常见示例),格式为ERRORID:0xdeadbeef

  • 第二个errors.txt包含成对存储的错误消息LONG_ERROR_DESCRIPTION,0xdeadbeef

我试图使用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 个答案:

答案 0 :(得分:2)

您可以从错误消息目录创建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方言,所以这可能需要稍微调整一下。 Linux上的sed我认为应该在正则表达式中对括号进行分组之前使用反斜杠,并且很乐意容忍标准输入作为-f选项的参数。但是这对其他Unices来说是不可移植的(但是如果你需要可移植性,你可以用Perl代替Perd)。

* 编辑:如果错误消息是相当静态的,并且/或者您想要从标准输入读取日志,请将生成的脚本保存在文件中;

# 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 -ferrors.sed的顶部添加chmod +x,以使其成为自包含的命令脚本。

答案 1 :(得分:1)

我不知道这是否有效,因为我无法得到关于捕获组是否仍然存在的答案,但是there is a lot more to sed than just the s command。我以为你可以在正则表达式行选择器中使用捕获组,然后使用它来进行命令替换。像这样:

/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

答案 2 :(得分:1)

使用gensub()的GNU awk和match()的3rg arg:

$ 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是另一个子集的子集时也不会失败,例如如果&\1是2个单独的错误代码,则sed脚本可能会失败,具体取决于它们在errors.txt中的显示顺序,例如他们可以将0xdead转换为0xdeadbeef。首先映射ERRORS:0xdeadbeef

答案 3 :(得分:0)

只是让人们寻找裸壳和sed的解决方案。不完美但工作:

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

如果您看到一些修复选项,请分享。