![如何使用shell脚本从多个文件中解析特定字符串](https://linux22.com/image/189885/%E5%A6%82%E4%BD%95%E4%BD%BF%E7%94%A8shell%E8%84%9A%E6%9C%AC%E4%BB%8E%E5%A4%9A%E4%B8%AA%E6%96%87%E4%BB%B6%E4%B8%AD%E8%A7%A3%E6%9E%90%E7%89%B9%E5%AE%9A%E5%AD%97%E7%AC%A6%E4%B8%B2.png)
我正在开发一个 esp32 项目,并将所有 esp32 uart 调试输出记录在多个文件中。在某些时候,我的早期版本崩溃了,输出看起来像这样:
Guru Meditation 错误:核心 0 发生恐慌(缓存已禁用,但已访问缓存内存区域)。
核心 0 寄存器转储:PC:0x40008150 PS:0x00060034 A0:0x8008225c A1:0x3ffb0670 A2:0x3f40117c
A3:0x00000000 A4:0x00000004 A5:0x00000000
A6:0x00000000 A7:0xfffffff7 A8:0x00000000 A9:0x00000000
A10:0x000000ad A11:0x00000000 A12:0x800861de A13:0x3ffbb010
A14:0x00000003 A15:0x00060a23 SAR:0x00000020 原因:0x00000007
EXCVADDR:0x00000000 LBEG:0x4000c2e0 借出:0x4000c2f 6 LCOUNT:0x00000000
核心 0 正在 ISR 上下文中运行:EPC1:0x4008a2e8 EPC2:0x00000000 EPC3:0x00000000 EPC4:0x40008150回溯:0x4000814d:0x3ffb0670 0x40082259:0x3ffb0700 0x400833b9:0x3ffb0720 0x400833e2:0x3ffb0740 0x40081e6a:0x3ffb0760 0x4008a2e5:0x3 ffbb0a0 0x4008438d:0x3ffbb0c0 0x400830ed:0x3ffbb0e0 0x40083271:0x3ffbb110 0x400de4b1:0x3ffbb130 0x400dd525:0x3ffbb150 0x400dd7c5:0x3ffbb170 0x 400de175:0x3ffbb1d0 0x400dccf2:0x3ffbb240 0x400dcedf:0x3ffbb2a0 0x400dcf47:0x3ffbb2d0 0x400dbebf:0x3ffbb2f0 0x400dbed2:0x3ffbb310 0x400d757a:0x3ffbb330 0x400d770a:0x3ffbb360 0x400d3a66:0x3ffbb380
ELF 文件 SHA256:c278c3c874dd3748
正在重启...
此时,我可以开始在回溯指向的位置检查我的代码:
addr2line xtensa-esp32- -pfiaC -e project.elf 0x400d3a66 0x400d770a 0x400d757a ...(the remaining addresses of the backtrace)
输出如下:
0x400d3a66:root_task 位于 $somePATH/build/../main/main.c:163
0x400d770a:在 $somePATH/build/../main/init_app.c:122 处初始化
0x400d757a:init_nvs_app位于$somePATH/build/../main/init_app.c:47
(内联) init_nvs_app 位于 /$somePATH/build/../main/init_app.c:34
这很乏味。所以我需要自动化它,但我对shell脚本没有太多经验(linux新手)
还有数百万种不同的方法来实现它。有经验的人可以帮我 grep “大师冥想错误:”和“重新启动...”之间的文本吗?将 PC(程序计数器)地址与回溯分离成一行并忽略 SP(堆栈)的最佳方法是什么?指针)(在此特定情况下,SP 地址以 0x3ff 开头)。我已经尝试过使用 grep 正则表达式和 awk 进行一些操作,但还不够好。
答案1
sed
版本:
sed -n -E -e '/^Backtrace:/ {s/Backtrace://; s/:0x3ff[^ ]* */ /g; p}' input.txt
这会忽略所有不以“Backtrace:”开头的行,并使用两个搜索和替换规则在打印输出之前修改匹配的行。
该s/:0x3ff[^ ]* */ /g
操作匹配以 开头、:0x3ff
后跟零个或多个非空格字符 ( [^ ]*
)、后跟零个或多个空格 ( *
) 的任何文本,并替换用单个空格字符找到的任何匹配项。是全局查找替换( /g
),所以会影响全部在线找到的匹配项,而不仅仅是第一个匹配项。
它并不试图将其行动限制在“古鲁冥想”和“重新启动”之间。如果输入中没有任何其他以“Backtrace:”开头的行,或者一次只提供一个输入文件,这应该足够了。
如果0x3ff
多次运行中的 不恒定,则可以将其更改为匹配冒号后跟0x
1 个或多个十六进制数字的正则表达式。例如
s/:0x[0-9a-f]+//gi
如果需要,此更改也可以与下面的 awk 和 perl 版本一起使用。
awk
版本:
awk -v SKIP=1 '/^Guru Meditation Error/ { SKIP=0 };
/^Rebooting\.\.\./ { SKIP=1} ;
SKIP==1 || ! /^Backtrace:/ { next };
{
sub("^Backtrace:","");
gsub(":0x3ff[^ ]* *"," ");
print
}
' input.txt
这会跳过所有不在“Guru Meditation”和“Rebooting”行之间的输入,以及所有不以“Backtrace:”开头的输入。然后,它在打印出修改后的行之前,使用两个搜索和替换操作(sub()
和)修改非跳过行。gsub()
珀尔版本:
perl -lne 'BEGIN {$SKIP=1};
$SKIP=0 if (m/^Guru Meditation Error/);
$SKIP=1 if (m/^Rebooting\.\.\./);
next if ($SKIP || ! m/^Backtrace:/);
s/Backtrace://;
s/:0x3ff\H*\h*/ /g;
print' input.txt
它的工作原理与 awk 版本类似,但代码看起来像是 awk 和 sed 版本之间的交叉(perl 能够实现类似 awk 和类似 sed 的语法......以及更多)。
最终s//g
操作已修改为使用 perl 的\H
(非水平空格字符)和\h
(水平空格字符)匹配,而不仅仅是空格(即制表符和空格等 -man perlrecharclass
有关详细信息,请参阅并搜索“空白”部分)
所有三个版本的输出都是相同的:
0x4000814d 0x40082259 0x400833b9 0x400833e2 0x40081e6a 0x4008a2e5 0x4008438d 0x400830ed 0x40083271 0x400de4b1 0x400dd525 0x400dd7c5 0x400de175 0x400dccf2 0x400dcedf 0x400dcf47 0x400dbebf 0x400dbed2 0x400d757a 0x400d770a 0x400d3a66
所有三个版本都能够从标准输入或命令行上的一个或多个文件名获取输入。上面的示例使用input.txt
(这是我将示例输入复制粘贴到的文本文件)。
它们都输出到 stdout,因此可以用于命令替换。例如
addr2line xtensa-esp32- -pfiaC -e project.elf $(sed -n -E -e '/^Backtrace:/ {s/Backtrace://; s/:0x3ff[^ ]* */ /g; p}' input.txt)