sed 在下一行中复制子字符串

问题描述

我有一个 .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. 我很确定存在一行或更优雅的解决方案。

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

解决方法

您可以考虑使用更好的工具 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
,

使用 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.
,

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

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

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

,

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

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

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

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

您可以使用保留空间:

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

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

msgstr

另请注意,您通常如何将多个 sed -e '/^msgid "/h' -e '/^msgstr ""/!b' \ -e x -e 's/^msgid/msgstr/' it.po >it2.po 语句与 sed 组合在一起,而不是创建一个新文件,然后对其重复运行 -esed -i 是一种脚本语言;想用就学。

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

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

sed
,

保持简单并使用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) && awk 'script' file > "$tmp" && mv "$tmp" file命令。