【问题标题】:sed copy substring in following linesed 在下一行复制子字符串
【发布时间】:2021-08-19 09:42:13
【问题描述】:

我有一个 .po 文件,如果 msgstr 为空,我需要将 msgid 值复制到 msgstr 值中。

例如

msgid "Hello"
msgstr ""

msgid "Dog"
msgstr "Cane"

应该变成

msgid "Hello"
msgstr "Hello"

msgid "Dog"
msgstr "Cane"

目前,出于测试目的,我正在处理另一个文件,但最终脚本将内联运行。

#!/bin/bash
rm it2.po
sed $'s/^msgid.*/&\\\n---&/' it.po > it2.po
sed -i '/^msgstr/d' it2.po
sed -i 's/^---msgid/msgstr/' it2.po

这个脚本有两个问题(至少):

  1. 当 msgstr 不为空时,也将 msgid 复制到 msgstr;
  2. 我很确定存在单行或更优雅的解决方案。

任何帮助将不胜感激。提前致谢。

【问题讨论】:

  • sed -i -E '/^msgid ".*"$/{N;s/^(msgid "(.*)"\nmsgstr )""$/\1"\2"/}' file
  • “不适用于超过 70 个字符的行”问题可能无法重现。 sed 开箱即用不会做类似的事情,尽管一些非常古老的 sed 实现可能有最大行长度(尽管可能要长得多)。
  • @tripleee 你是对的,问题与 sed 无关。这是由于在 sed 之前调用了 xgettext、msginit 和 msgmerge 命令。问题已更新
  • 别担心final script will works inline - 任何命令(sed、perl、ruby、gawk 等)中的-i 只是语法糖,它并没有真正进行内联编辑,它使用幕后的临时文件。如果您的 sed 或任何其他命令没有用于伪就地编辑的 -i,您也可以轻松地使用 tmp=$(mktemp) && sed 's/old/new/' file > "$tmp" && mv "$tmp" file

标签: sed po


【解决方案1】:

您可以考虑使用更好的工具gnu awk 而不是sed

awk -i inplace -v FPAT='"[^"]*"|\\S+' '$id != "" && $1 == "msgstr" && (NF==1 || $2 == "\"\"") {$2=id} $1 == "msgid" {id=$2} 1' file

msgid "Hello"
msgstr "Hello"

msgid "Dog"
msgstr "Cane"

-v FPAT='"[^"]*"|\\S+' 使带引号的字符串或任何非空白字段成为单个字段。

更易读的形式:

awk -i inplace -v FPAT='"[^"]*"|\\S+' '
$id != "" && $1 == "msgstr" && (NF==1 || $2 == "\"\"") {$2=id}
$1 == "msgid" {id=$2}
1' file

【讨论】:

    【解决方案2】:

    您可以使用保持空间:

    sed '
        /^msgid[\t ]*/ {
            p
            s///
            x
            d
        }
        /^msgstr[\t ]*""/ {
            x
            s/^/msgstr /
        }
    ' <in.po >out.po
    
    • 如果行以msgid 开头
      • 打印出来
      • 删除关键字
      • 保存字符串以保留
      • 转到下一行
    • else 如果行以msgstr 开头并且值为空
      • 从保持中检索字符串
      • 添加关键字
    • 隐式打印

    【讨论】:

      【解决方案3】:

      这是一个简单的sed 脚本,它将最新的msgid 保留在保留空间 (h) 中,然后将其带回 (x) 并在看到空的 msgstr 时将其更改为 msgstr .

      sed -e '/^msgid "/h' -e '/^msgstr ""/!b' \
          -e x -e 's/^msgid/msgstr/' it.po >it2.po
      

      还要注意您通常如何将多个 sed 语句与 -e 组合在一起,而不是创建一个新文件然后在其上重复运行 sed -ised 是一种脚本语言;想用就学吧。

      (一些sed 变体不能容忍这种安排;如果您对此有问题,可以将脚本组合成一个字符串,语句之间使用分号。)

      话虽如此,sed 在很大程度上是一种只写语言。也许您最好使用简单的 Awk(或 Python 等)解决方案。

      awk '/^msgid "/ { s=$0; sub(/^msgid/, "", s) }
          /^msgstr ""/ { $0 = $1 s } 1' it.po >it2.po
      

      【讨论】:

      • 甚至sed '/^msgid/h; /^msgstr ""/{x;s/id/str/}'
      • 是的,我倾向于避免使用大括号,因为不同的方言对于如何将它们与其他语句结合起来的规则略有不同。您的公式在 macOS 上给了我“替代命令中的错误标志”。在} 之前添加一个分号可以解决这个问题,但是......我所说的。
      【解决方案4】:

      使用 GNU awk 并仅显示示例,我们可以尝试跟随。

      awk -v RS='"[^"]*"|\n+' '
      RT=="\n"{ next }
      $0~/^msgstr/{
        if(RT=="\"\""){ $0=$0 val }
        else          { $0=$0 RT  }
      }
      $0~/^msgid/     { val=RT
                        $0=$0 RT  }
      RT
      '  Input_file
      


      第二个解决方案: 与上面的解决方案略有不同,上面只需要出现 1 或 2 次 " 但这将一直有效,直到第一次出现新行" 在一行中,然后以下将有所帮助,再次使用所示示例进行编写和测试。

      awk  -v RS='"[^\n]*|\n+' '
      RT=="\n"{ next }
      $0~/^msgstr/{
        if(RT=="\"\""){ $0=$0 val }
        else          { $0=$0 RT  }
      }
      $0~/^msgid/     { val=RT
                        $0=$0 RT  }
      RT
      '  Input_file
      

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

      awk  -v RS='"[^"]*"|\n+' '    ##Starting awk program from here and setting record separator as " till " comes or new lines.
      RT=="\n"{ next }              ##If RT is newline then take cursor to next line.
      $0~/^msgstr/{                 ##Checking if line starts from msgstr then:
        if(RT=="\"\""){ $0=$0 val } ##Checking if RT us "" then add val to current line.
        else          { $0=$0 RT  } ##Else simply add RT.
      }
      $0~/^msgid/     { val=RT      ##Checking if line starts from msgid then make val to RT
                        $0=$0 RT  } ##Adding RT to $0.
      RT                            ##Printing line if RT is not null.
      ' Input_file                  ##Mentioning Input_file name here.
      

      【讨论】:

        【解决方案5】:

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

        sed -E 'N;s/(msgid "(.*)".*msgstr )""/\1"\2"/;P;D' file
        

        打开一个两行窗口,如果第一行包含msgid,第二行包含msgstr "",则将msgstr 值替换为msgid 值。打印/删除第一行并重复。

        【讨论】:

          【解决方案6】:

          由于输入文件的结构如此简单和一致,我认为以下内容应该足够了(它适用于您提供的 3 个示例):

          sed -zE 's/(msgid "([^"]+)"\nmsgstr ")"/\1\2"/g' your_file
          
          • -z 使文件成为嵌入\ns 的长输入字符串,因此我们不需要ND 或其他命令,因为整个文件已经在模式空间中;
          • -E 让我们使用 ()+ 代替 \(\)\+(以及其他类似的东西)
          • 最外层的() 捕获msgid "Hello"\nmsgstr "(闭合的" 匹配但未捕获);
          • 最里面的() 捕获第一个双引号字符串;
          • \1\2" 连接匹配的文本(除了最后的 ",如上所述),前两个 "s 和结束 " 之间的文本,
          • 标志 g 将在整个文件中应用替换。

          如果前导字符串不是那么重要(例如,它们始终相同,并且行始终显示为 msgid 后跟 msgstr),您可以再压缩一下上面的命令:

          sed -zE 's/(([^"]+)"\n[^\n]*")"/\1\2"/g' your_file
          

          【讨论】:

            【解决方案7】:

            保持简单并使用 awk,例如在每个 Unix 机器上的任何 shell 中使用任何 awk:

            $ awk '$2~/""/{$2=p} {p=$2} 1' it.po
            msgid "Hello"
            msgstr "Hello"
            
            msgid "Dog"
            msgstr "Cane"
            

            如果这还不是您所需要的,那么编辑您的问题以提供更全面的示例输入/输出,包括不适用的案例。

            既然您已经为-i 安装了 GNU sed,如果您想要“就地”编辑,您也可以为-i inplace 安装 GNU awk,或者像对任何其他命令一样执行 tmp=$(mktemp) &amp;&amp; awk 'script' file &gt; "$tmp" &amp;&amp; mv "$tmp" file

            【讨论】:

              猜你喜欢
              • 2017-05-08
              • 1970-01-01
              • 2011-02-20
              • 1970-01-01
              • 2013-05-13
              • 2014-11-05
              • 1970-01-01
              • 2011-01-08
              • 1970-01-01
              相关资源
              最近更新 更多