【问题标题】:bash extract segments of a string and store in variablesbash提取字符串的片段并存储在变量中
【发布时间】:2019-09-22 16:42:50
【问题描述】:

我想将 cppclean 的输出转换为类似 cppcheck 的 xml 部分,这样:

./bit_limits.cpp:25: static data 'bit_limits::max_name_length'

变成:

<error id="static data" msg="bit_limits::max_name_length">
    <location file="./bit_limits.cpp" line="25"/>
</error>

我从一些 awk 开始:

测试代码:

echo "./bit_limits.cpp:25: static data 'bit_limits::max_name_length'" > test
cat test.out | awk -F ":" '{print "<error id=\""$3"\""}
                           {print "msg=\""}{for(i=4;i<=NF;++i)print ":"$i}{print "\">"}
                           {print "<location file=\""$1"\" line=\""$2"\"/>"}
                           {print "</error>"}'

注意:要运行此命令,您需要将 cat 命令放回一行 - 为了便于阅读,我将其打印在多行中。

解释: 我正在使用awk 并用冒号“:”分隔 - 它将行分成有用的块,我尝试将它们构建到 XML 中:

  • {print "&lt;error id=\""$3"\""} - 提取错误 ID 部分
  • {print "msg=\""}{for(i=4;i&lt;=NF;++i)print ":"$i}{print "\"&gt;"} - 提取消息(替换缺少的冒号,这是所有剩余部分
  • {print "&lt;location file=\""$1"\" line=\""$2"\"/&gt;"} - 提取文件和行,这部分很容易,因为冒号排列得很好
  • {print "&lt;/error&gt;"} - 最后打印结束标签

这很接近,但并不完全正确,它会产生:

<error id=" static data 'bit_limits"
msg="
:
:max_name_length'
">
<location file="./bit_limits.cpp" line="25"/>
</error>

id 字段应该只是“静态数据”,msg 字段应该是“'bit_limits::max_name_length'”,但除此之外没关系(目前我不介意它被拆分为多行 - 尽管我希望 awk 每次都不要打印新行。

更新 正如@charlesduffy 指出的那样——对于上下文——我想在 bash 中执行此操作,因为我想将此代码嵌入到一个 makefile(或只是一个普通的 bash 脚本)中以获得最大的可移植性(即不需要 python 或其他工具)。

【问题讨论】:

  • 请注意,一般来说,使用字符串操作工具来解析或生成结构化数据是非常糟糕的做法。使用真正的 XML 解析器;您可以从 bash(XMLStarlet,但调用 Python 函数也很容易)调用很多好的方法,即使您的数据以语义等效但文本不同的格式编写,它也将继续工作生成它的工具的下一个版本。
  • @CharlesDuffy 感谢您的建议:)。在这种情况下,我真的想把它嵌入到一个makefile中。我不希望用户只有基本的 bash、sed、awk 等任何先决条件(如 python 或其他)......它就是这样一个简单的 XML
  • 我用来在一次传递中有效地将单个流读取到多个 shell 变量中的常用方法不工作(或者,更确切地说,不允许那些shell 变量作为不同的 make 变量持久化)在 makefile 的上下文中(其中每个操作由不同的 shell 实例运行),因此答案在 makefile 上下文中应该是有用的,这是要添加的重要上下文。顺便说一句,您是在分配 SHELL=bash,还是您的 shell 真的是 sh(默认情况下使用 make)并且问题被错误标记?
  • (旁白:就我个人而言,我使用Nix 来组装构建系统;它会为您完成组装依赖项的所有工作,因此使用 Nix 描述,而不是 Makefile,您可以拥有任何您需要的依赖项——Python 模块或整个 Python 解释器、JavaScript 编译器、应用了特定补丁的 coreutils、非常特定的 bash 版本、anything——由构建过程本身以完全可重现的方式提供) .
  • @CharlesDuffy 是的,这是真的 - 我正在使用 bash 专门用于我的 makefile。我们为我们的 makefile 设置了一个很好的设置,这是对它的一个小调整,所以这就是我真的想坚持使用它的原因:)

标签: linux bash awk


【解决方案1】:

使用 bash 和正则表达式:

x="./bit_limits.cpp:25: static data 'bit_limits::max_name_length'"
[[ $x =~ (.+):([0-9]+):\ (.+)\ \'(.+)\' ]]

declare -p BASH_REMATCH

输出:

声明 -ar BASH_REMATCH='([0]="./bit_limits.cpp:25: 静态数据 '\''bit_limits::max_name_length'\''" [1]="./bit_limits.cpp" [2]= "25" [3]="静态数据" [4]="bit_limits::max_name_length")'

数组 BASH_REMATCH 中的元素 1 到 4 包含搜索到的字符串。

来自man bash

BASH_REMATCH:一个数组变量,其成员由=~ 二元运算符分配给[[ 条件命令。索引为 0 的元素是字符串中匹配整个正则表达式的部分。索引为 n 的元素是字符串中匹配第 n 个带括号的子表达式的部分。这个变量是只读的。

【讨论】:

  • 你能帮我分解一下吗? - 为什么是双['s?我还假设 BASH_REMATCH 是一些特殊的 bash 变量。 IIRC 括号里的东西(...) 变成了参数? - 所以它正在寻找:1.“:”之前的任何内容 2.冒号之前的任何数字 3.然后是任何数字 4.然后是单引号中的任何内容......并且所有输出都以某种方式神奇地存储在一个名为 BASH_REMATCH 的数组中! ...它非常甜蜜,我想我已经接近理解它了,但我们将不胜感激:)
  • 你在正确的轨道上。我希望this article 能帮到你。使用 [[ ... ]]=~ 可以将字符串(左侧,带或不带引号)与正则表达式(右侧,始终不带引号)匹配。
  • 太酷了!...再见sed!我现在有一个该脚本的副本是我的标准库,它们也应该成为 bash 的一部分 :)
【解决方案2】:

可能比它需要的更复杂:

awk '{
    split($1, file_line, ":")
    field = 2
    while(substr($field, 1, 1) != "'\''") {
        id = id " " $field
        ++field
    }
    id = substr(id, 2)
    while(field <= NF) {
        msg = msg " " $field
        ++field
    }
    msg = substr(msg, 3, length(msg) - 1)
    printf("<error id=\"%s\" msg=\"%s\">\n", id, msg)
    printf("    <location file=\"%s\" line=\"%s\">\n", file_line[1], file_line[2])
    print "</error>"
}' test.out

【讨论】:

  • 比我的尝试更好 - 谢谢,还学到了更多 awk 技巧:)
猜你喜欢
  • 2020-08-11
  • 2022-01-06
  • 1970-01-01
  • 2015-05-07
  • 2022-01-12
  • 2023-01-12
  • 2021-12-23
  • 2017-09-27
  • 1970-01-01
相关资源
最近更新 更多