【问题标题】:Modify numeric values in strings from stdout从标准输出修改字符串中的数值
【发布时间】:2020-05-25 02:25:27
【问题描述】:

我有想要重写和修改一些数值的输入字符串:

输入src/main.tsx(2,31): error TS2304: Cannot find name 'foo'.

所需输出src/main.tsx:1:30: error: TS2304: Cannot find name 'foo'.

注意:

  • 数值减1。
  • error 是动态的。也可以是warning
  • 该命令会将输入通过管道传输给它 (tsc | MAGIC_HAPPENS_HERE)。如果出现错误,则此命令应将错误传递出去。

到目前为止,我有以下内容: sed -E "s/^([^(]+)\(([0-9]+),([0-9]+)\): ((warning)|(error)) (.*)/\1:\2:\3: \4: \7/"

这工作除了数字操作。根据我的阅读,我相信sed 不是适合这项工作的工具。我查看了awk,但遇到了正则表达式捕获组。

我正在使用 MacOS。该命令不需要是可移植的。我很高兴使用 brew 安装其他工具。

【问题讨论】:

    标签: shell awk sed text-parsing


    【解决方案1】:

    请您尝试关注一下。

    awk '
    match($0,/\([^)]*/){
      value=substr($0,RSTART+1,RLENGTH-1)
      num=split(value,array,",")
      for(i=1;i<=num;i++){
        val=(val?val":":"")array[i]-1
      }
      part_2=substr($0,RSTART+RLENGTH+1)
      sub(/error/,"error:",part_2)
      print substr($0,1,RSTART-1) ":" val part_2
      value=part_2=""
    }'  Input_file
    

    输出如下。

    src/main.tsx:1:30: error TS2304: Cannot find name 'foo'.
    

    说明:为上述代码添加详细说明。

    awk '                                          ##Starting awk program here.
    match($0,/\([^)]*/){                           ##Using match function to match regex from ( till ) in line.
      value=substr($0,RSTART+1,RLENGTH-1)          ##Creating variable value which has value of sub-string from RSTART+1 to RLENGTH-1.
      num=split(value,array,",")                   ##Using split, to split the value into an array named array.
      for(i=1;i<=num;i++){                         ##Running for loop from i=1 to till value of num(which is length of array).
        val=(val?val":":"")array[i]-1              ##Creating variable val whose value is subtraction of array[i] value with 1 and keep concatenating to its own value.
      }
      part_2=substr($0,RSTART+RLENGTH+1)           ##Creating variable part_2 whose value is rest of line after matched regex.
      sub(/error/,"error:",part_2)                 ##Substituting string error with error: here in rest of the line.
      print substr($0,1,RSTART-1) ":" val part_2   ##Printing sub-string from 1 to till match found, :, val and part_2 variables here.
      value=part_2=""                              ##Nullify variables value and part_2 here.
    }'  Input_file                                 ##Mentioning Input_file name here.
    

    【讨论】:

    • 差不多了,谢谢。在error 之后缺少一个冒号。您可以在答案中添加一些 cmets 吗?以目前的形式,我无法理解它是如何工作的,因此无法对其进行编辑。
    • @BenedictCohen,我现在按照要求编辑了答案。同样在上面的代码中,如果整行中仅存在 1 个错误,则可以进行一项更改,然后尽管设置了 part_2 变量,也可以进行简单的替换。正在编写的说明也会在几分钟内通知您。
    • @BenedictCohen,我已经为代码添加了详细级别的解释,请检查并让我知道这是否对您有帮助。
    【解决方案2】:

    我同意sed 不是合适的工具。由于过时(或者可能只是过时),我会使用 Perl:

    $ cat data
    src/main.tsx(2,31): error TS2304: Cannot find name 'foo'.
    $ perl -p -e 's/^(.*?)\((\d+),(\d+)\): (\w+) /sprintf("%s:%d:%d: %s: ", $1, $2-1, $3-1, $4)/e' data
    src/main.tsx:1:30: error: TS2304: Cannot find name 'foo'.
    $
    

    正则表达式延迟匹配“(nn,mmm): ”之前的所有内容,后跟一个“单词”,捕获两个数字以及括号和单词之前的内容。然后它使用/e 修饰符('将右侧计算为表达式' - 请参阅Regexp Quote-Like Operators)使用sprintf() 进行减法运算以格式化信息。 “单词”将捕获errorwarning 或其他所有字母后跟空格的内容。您可以使用\S+ 代替\w+ 来捕获任何非空格字符序列。我假设分隔符是单个空格;如果需要,您可以使用\s+ 代替空白,以匹配任何非空的空白序列。 (-p 选项只是意味着'从命名文件中读取行,如果没有文件被命名,则为标准输入,执行-e '…script…' 中的操作并打印结果。)

    使用 5.18.4(macOS Mojave 10.14.6 上的/usr/bin/perl)和 5.30.0 测试。


    如果您有一个产生错误的进程,那么您需要确保将错误发送到 Perl 脚本——这是 shell 脚本,而不是其他任何东西。

    tsc 2>&1 |
    perl -p -e 's/^(.*?)\((\d+),(\d+)\): (\w+) /sprintf("%s:%d:%d: %s: ", $1, $2-1, $3-1, $4)/e'
    

    如果您需要命令的标准输出(修改后的问题和上面的 shell 脚本片段中的tsc)去其他地方,那么您需要小心(另见How to pipe stderr and not stdout),但也许:

    tsc 2>&1 >tsc.out |
    perl -p -e 's/^(.*?)\((\d+),(\d+)\): (\w+) /sprintf("%s:%d:%d: %s: ", $1, $2-1, $3-1, $4)/e'
    

    管道最初将标准输出设置为 Perl; 2&gt;&amp;1 也在那里发送标准错误; &gt;tsc.out 更改标准输出,使其进入文件tsc.out,将标准错误留在管道中。

    【讨论】:

    • 我喜欢这种方法,因为它使用了我更熟悉的工具(特别是regexprintf)。缺点是它吞下了错误。我已更新问题以包含更多详细信息。
    • 我不确定您所说的“吞下错误”是什么意思,但是基本的 Perl 将发送到标准输入的所有内容中继到标准输出,只修改包含它匹配的模式的行。我在所需的 shell 脚本上添加了一些注释,以便 Perl 可以读取管道中上一个命令在标准错误上写入的错误。我不清楚这是否是您困惑的根源。
    • 对不起,我不清楚。 “吞下错误”是指tsc 的返回值非零,但 perl 命令的返回值为 0。这意味着脚本在实际失败时报告为成功。
    • 在 Bash 中,set -o pipefail(而不是之前建议的 shopt -s pipefail)。它会导致管道返回失败的最右边命令的非零状态(因此管道只有在每个命令都成功时才成功)。
    猜你喜欢
    • 1970-01-01
    • 2016-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-01
    • 1970-01-01
    相关资源
    最近更新 更多