【问题标题】:Command substitution within sed expressionsed 表达式中的命令替换
【发布时间】:2011-12-01 17:49:28
【问题描述】:

我在使用 bash/sed 时遇到了一点问题。我需要能够在 sed 表达式中使用命令替换。我有两个大文本文件:

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

  • second 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 表达式或以其他方式构建,但我想避免多次解析这些文件(它们可能很大)。

一如既往地非常感谢您的帮助。

*日志文件中没有真正的格式。没有不一致地使用节、列、制表符/逗号分隔

附言。只是为了解释。以下表达式有效,但当然其中没有传递参数:

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

【问题讨论】:

  • errors.txt 的设计并不完全有利于机器加工。像0xdeadbeef Long description 这样的东西会更容易处理。也许你会想在另一个晴朗的日子从中生成一堆#defines?
  • @tripleee - 实际上我有一堆 #define 被 cmets 切得很厉害 - 这是我的真实情况。我试图尽可能地限制这个例子,以免进入细节。重要的是要有类似#define 的设置:TEXT 0xdeadbeef。这就是为什么您的解决方案也需要在我这边进行一些调整的原因。我必须收集所有头文件并从中生成 *.sed 文件。我正在努力,但你的方法尽可能接近可用的东西。

标签: linux bash sed substitution


【解决方案1】:

您可以从错误消息目录创建一个 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 代替 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

您还可以在errors.sedchmod +x 的顶部添加#!/usr/bin/sed -f,使其成为一个独立的命令脚本。

【讨论】:

  • 感谢分享。这个例子的问题是我更喜欢先获取我的日志文件 - errors.txt 在文件中,而日志有时来自管道。
  • 然后在文件中生成一个静态脚本。我会用 sn-p 更新。
  • 太棒了。奇迹般有效。我不得不稍微调整一下以匹配现实生活场景(不是这个一般示例),我担心 errors.txt 的大小会是一个问题,但它比基于 python 或 perl 的方法更快。我仍然需要检查 errors.txt 的更新频率,并考虑这个解决方案是否真的有效。非常感谢!
【解决方案2】:

我不知道这是否可行,因为我无法得到关于捕获组是否持续存在的答案,但 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

【讨论】:

  • @tripleee 请详细说明(您的意思是在 thatScript.pl 通话中吗?因为是的,我已经解决了这个问题。
  • 第一个例子不起作用。这是因为(我想)命令替换发生在 first - 这也是我的方法的问题。换档可能是一种选择,但我希望有一些关于 awk 或 sth 的指示。相似的。 Perl 对我来说可能很好,但不一定在客户端环境中。无论如何,感谢您提供漂亮的 perl sn-p。
  • @tripleee 啊,我明白你现在的意思了。是的,grep 完全能够打开文件本身。
  • @tripleee 我不同意这种说法。很多时候,像上面这样的例子都与管道一起使用。并且在开头放一些猫更容易,这样您以后就可以删除它并使用sn-p。 OTOH 很明显,在脚本内部需要进行一些优化。
【解决方案3】:

只是为了让人们寻找裸壳和 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

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

【讨论】:

    【解决方案4】:

    使用 GNU awk 用于 gensub() 和 3rg arg 用于 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 的各种可能内容,例如%&amp;\1,它不会失败,也不会失败给定的 ERRORID 是另一个的子集,例如如果 0xdead0xdeadbeef 是 2 个单独的错误代码,则 sed 脚本可能会失败,具体取决于它们在 errors.txt 中出现的顺序,例如他们可以将ERRORS:0xdeadbeef 转换为ERRORS:LONG_ERROR_DESCRIPTIONbeef。首先映射0xdead

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2015-04-01
      • 1970-01-01
      • 2012-10-29
      • 2013-10-11
      • 2020-07-12
      • 2018-08-22
      相关资源
      最近更新 更多