【问题标题】:defaulting unmatched backrefrences in perl or sed在 perl 或 sed 中默认不匹配的反向引用
【发布时间】:2021-02-05 23:54:28
【问题描述】:

有没有办法在这里默认返回引用变量$1$2$3

start="a" hi="1"  bye="2"
start="b" bye="3"
start="c" hi="4"

我正在使用这个命令过滤掉:

perl-ne 'print if s/.*start="([^"]+).*?hi="([^"]+).*?bye="([^"]+).*/$1 $2 $3/g'
a 1 2

有没有办法生成以下结果:

a 1 2
b null 3
c 4 null

我还搜索了默认后引用变量,但没有关于该变量的有效解决方案。例如,在 bash 中,我们使用 ${var:-null}var 默认为字符串 null

【问题讨论】:

  • 不,反向引用总是指向输入字符串中的文本。使用代码逻辑来操作匹配。在 Perl 中,使用 e 标志可以在 RHS 中使用代码,并调整模式以匹配可选字段。

标签: regex perl sed


【解决方案1】:

类似:

$ perl -nE 'my %vals=();
            while (m/(\w+)="([^"]+)"/g) { $vals{$1} = $2 }
            printf "%s %s %s\n", $vals{start}, $vals{hi}//"null", $vals{bye}//"null"
           ' input.txt
a 1 2
b null 3
c 4 null

将输入拆分为单独的键/值对,将它们保存在哈希表中,然后使用 // 运算符打印出值,如果已定义,则返回左侧参数,否则返回右侧参数。


如果 starthibye 是您可以拥有的唯一键并且它们始终按该顺序出现,则变化:

$ perl -ne 'm/start="([^"]+)"(?:\s+hi="([^"]+)")?(?:\s+bye="([^"]+)")?/;
            printf "%s %s %s\n", $1, $2//"null", $3//"null"' input.txt
a 1 2
b null 3
c 4 null

使 hi 和 bye 部分成为可选匹配的更丑陋的正则表达式。

【讨论】:

    【解决方案2】:

    这可能对你有用(GNU sed):

    sed -E 's/^/start=null hi=null bye=null\n/  # insert a template
            :a                                  # loop name
            s/(\S+=)\S+(.*\n.*)\1"(\S+)"/\3\2/  # replace lookup with value
            ta                                  # repeat till failure
            s/\S+=//g                           # remove any template
            P                                   # print
            d' file                             # delete debris
    

    插入模板并循环将匹配项替换为原始值。

    当不再匹配时,从原始行中删除所有不匹配的模板键和碎片。

    【讨论】:

      【解决方案3】:

      当捕获组是可选的(否则,如果任何一个子模式失败,则整个匹配都会失败),即使它们的子模式不匹配,也会为 capture groups 引入特殊数字变量($1 等)。没有比赛的人留在undef

      例如,如果一个模式有三个可选的捕获组,比如(...)?,那么在正则表达式之后(或替换运算符中的匹配部分之后)将存在所有$1,$2,$3变量,如果他们的子模式不匹配,有些可能是undef? 仍然使这些正式匹配,因为该模式的出现次数为零)。

      然后测试每个$N,如果undef 将其替换为所需的短语(此处为'null'

      perl -wnE'/
          (?: start \s*=\s* "([^"]+)"\s* )?
          (?: hi    \s*=\s* "([^"]+)"\s* )?
          (?: bye   \s*=\s* "([^"]+)"\s* )? /x; 
          say join " ", map { $_//"null" } ($1,$2,$3)
      ' file
      

      (为了便于阅读,分行并隔开)由于每个术语具有相同的结构,因此可以从预期单词列表中更灵活地准备模式。

      对于给定的样本file 打印

      一个 1 2 b 无 3 c 4 空

      这对于特定情况和单行来说是一种过度杀伤力,但在可以与不同关键字集一起使用的更圆润的脚本中很有用,因为所有硬编码的输入都在输入数组的定义(@w

      perl -wnE'
          BEGIN { 
              @w = qw(start hi bye);  # keywords to form a pattern with
              $re = join " ", 
                    map { q{(?:} . $_ . q{\s*=\s*"([^"]+)"\s*)?} } @w;
          };  
          @m = /$re/x; 
          say join " ", map { $_//"null" } @m
      ' file
      

      对于给定的输入file,这会打印相同的内容。在bash shell 中,它可以简单地按原样复制粘贴;在其他外壳中,您可能需要将其重新排成一行,然后删除 cmets。 (作为命令行程序给出,“one”-liner,便于测试。)

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-12-01
        • 1970-01-01
        相关资源
        最近更新 更多