sed 复制下一行中的子串
sed copy substring in following line
我有一个 .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
这个脚本有 2 个问题(至少):
- 当 msgstr 不为空时,也将 msgid 复制到 msgstr 中;
- 我很确定存在单行或更优雅的解决方案。
如有任何帮助,我们将不胜感激。提前致谢。
您可以考虑使用更好的工具 gnu awk
而不是 sed
:
awk -i inplace -v FPAT='"[^"]*"|\S+' '$id != "" && == "msgstr" && (NF==1 || == "\"\"") {=id} == "msgid" {id=} 1' file
msgid "Hello"
msgstr "Hello"
msgid "Dog"
msgstr "Cane"
-v FPAT='"[^"]*"|\S+'
使带引号的字符串或任何非空白字段成为单独的字段。
更易读的形式:
awk -i inplace -v FPAT='"[^"]*"|\S+' '
$id != "" && == "msgstr" && (NF==1 || == "\"\"") {=id}
== "msgid" {id=}
1' file
您可以使用保留 space:
sed '
/^msgid[\t ]*/ {
p
s///
x
d
}
/^msgstr[\t ]*""/ {
x
s/^/msgstr /
}
' <in.po >out.po
- 如果行以
msgid
开头
- 打印出来
- 删除关键字
- 保存要保存的字符串
- 转到下一行
- else 如果行以
msgstr
开头且值为空
- 从保留中检索字符串
- 添加关键字
- 隐式打印
这是一个简单的 sed
脚本,它将最新的 msgid
保留在 space (h
) 中,然后将其取回 (x
) 并且如果它看到空 msgstr
.
,则将其更改为 msgstr
sed -e '/^msgid "/h' -e '/^msgstr ""/!b' \
-e x -e 's/^msgid/msgstr/' it.po >it2.po
还请注意您通常如何将多个 sed
语句与 -e
组合在一起,而不是创建一个新文件然后在其上重复 运行 sed -i
。 sed
是一种脚本语言;想用就学
(一些 sed
变体不能容忍这种安排;如果您对这个有问题,可以将脚本组合成一个字符串,语句之间用分号隔开。)
话虽如此,sed
在很大程度上是一种只写语言。也许使用简单的 Awk(或 Python 等)解决方案会更好。
awk '/^msgid "/ { s=[=11=]; sub(/^msgid/, "", s) }
/^msgstr ""/ { [=11=] = s } 1' it.po >it2.po
使用 GNU awk
并且仅显示示例,我们可以尝试以下操作。
awk -v RS='"[^"]*"|\n+' '
RT=="\n"{ next }
[=10=]~/^msgstr/{
if(RT=="\"\""){ [=10=]=[=10=] val }
else { [=10=]=[=10=] RT }
}
[=10=]~/^msgid/ { val=RT
[=10=]=[=10=] RT }
RT
' Input_file
第二个解决方案: 与上面的解决方案略有不同,上面只会出现 1 或 2 次 "
但是这将一直有效,直到新行来自一行中第一次出现的 "
,然后以下内容将有所帮助,再次编写并使用显示的示例进行测试。
awk -v RS='"[^\n]*|\n+' '
RT=="\n"{ next }
[=11=]~/^msgstr/{
if(RT=="\"\""){ [=11=]=[=11=] val }
else { [=11=]=[=11=] RT }
}
[=11=]~/^msgid/ { val=RT
[=11=]=[=11=] 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.
[=12=]~/^msgstr/{ ##Checking if line starts from msgstr then:
if(RT=="\"\""){ [=12=]=[=12=] val } ##Checking if RT us "" then add val to current line.
else { [=12=]=[=12=] RT } ##Else simply add RT.
}
[=12=]~/^msgid/ { val=RT ##Checking if line starts from msgid then make val to RT
[=12=]=[=12=] RT } ##Adding RT to [=12=].
RT ##Printing line if RT is not null.
' Input_file ##Mentioning Input_file name here.
这可能适合您 (GNU sed):
sed -E 'N;s/(msgid "(.*)".*msgstr )""/""/;P;D' file
打开两行 window,如果第一行包含 msgid
和第二行 msgstr ""
,则将 msgstr
值替换为 msgid
值. Print/delete 第一行并重复。
由于输入文件的结构如此简单和一致,我认为以下内容应该足够了(它适用于您提供的 3 个示例):
sed -zE 's/(msgid "([^"]+)"\nmsgstr ")"/"/g' your_file
-z
使文件成为嵌入了 \n
的一长串输入,因此我们不需要像 N
、D
或其他命令,因为整个文件已经在模式 space; 中
-E
让我们可以使用 (
、)
和 +
而不是 \(
、\)
和 \+
(以及其他类似的东西)
- 最外层的
()
捕获msgid "Hello"\nmsgstr "
(最后的"
匹配但未捕获);
- 最里面的
()
捕获第一个双引号字符串;
"
将匹配的文本(最后的 "
除外)与前两个 "
之间的文本和结束的 "
,
- 标志
g
将在整个文件中应用替换。
如果前导字符串不是那么重要(例如它们总是相同的,并且这些行总是显示为 msgid
后跟 msgstr
),您可以将上面的命令压缩更多一点:
sed -zE 's/(([^"]+)"\n[^\n]*")"/"/g' your_file
保持简单并使用 awk,例如在每个 Unix 机器上的任何 shell 中使用任何 awk:
$ awk '~/""/{=p} {p=} 1' it.po
msgid "Hello"
msgstr "Hello"
msgid "Dog"
msgstr "Cane"
如果这不是您需要的全部内容,请编辑您的问题以提供更全面的示例input/output,包括不适用的案例。
既然你有 -i
的 GNU sed,你也有或可以安装 GNU awk for -i inplace
如果你想要“就地”编辑,或者像你想要的那样做 tmp=$(mktemp) && awk 'script' file > "$tmp" && mv "$tmp" file
任何其他命令。
我有一个 .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
这个脚本有 2 个问题(至少):
- 当 msgstr 不为空时,也将 msgid 复制到 msgstr 中;
- 我很确定存在单行或更优雅的解决方案。
如有任何帮助,我们将不胜感激。提前致谢。
您可以考虑使用更好的工具 gnu awk
而不是 sed
:
awk -i inplace -v FPAT='"[^"]*"|\S+' '$id != "" && == "msgstr" && (NF==1 || == "\"\"") {=id} == "msgid" {id=} 1' file
msgid "Hello"
msgstr "Hello"
msgid "Dog"
msgstr "Cane"
-v FPAT='"[^"]*"|\S+'
使带引号的字符串或任何非空白字段成为单独的字段。
更易读的形式:
awk -i inplace -v FPAT='"[^"]*"|\S+' '
$id != "" && == "msgstr" && (NF==1 || == "\"\"") {=id}
== "msgid" {id=}
1' file
您可以使用保留 space:
sed '
/^msgid[\t ]*/ {
p
s///
x
d
}
/^msgstr[\t ]*""/ {
x
s/^/msgstr /
}
' <in.po >out.po
- 如果行以
msgid
开头- 打印出来
- 删除关键字
- 保存要保存的字符串
- 转到下一行
- else 如果行以
msgstr
开头且值为空- 从保留中检索字符串
- 添加关键字
- 隐式打印
这是一个简单的 sed
脚本,它将最新的 msgid
保留在 space (h
) 中,然后将其取回 (x
) 并且如果它看到空 msgstr
.
msgstr
sed -e '/^msgid "/h' -e '/^msgstr ""/!b' \
-e x -e 's/^msgid/msgstr/' it.po >it2.po
还请注意您通常如何将多个 sed
语句与 -e
组合在一起,而不是创建一个新文件然后在其上重复 运行 sed -i
。 sed
是一种脚本语言;想用就学
(一些 sed
变体不能容忍这种安排;如果您对这个有问题,可以将脚本组合成一个字符串,语句之间用分号隔开。)
话虽如此,sed
在很大程度上是一种只写语言。也许使用简单的 Awk(或 Python 等)解决方案会更好。
awk '/^msgid "/ { s=[=11=]; sub(/^msgid/, "", s) }
/^msgstr ""/ { [=11=] = s } 1' it.po >it2.po
使用 GNU awk
并且仅显示示例,我们可以尝试以下操作。
awk -v RS='"[^"]*"|\n+' '
RT=="\n"{ next }
[=10=]~/^msgstr/{
if(RT=="\"\""){ [=10=]=[=10=] val }
else { [=10=]=[=10=] RT }
}
[=10=]~/^msgid/ { val=RT
[=10=]=[=10=] RT }
RT
' Input_file
第二个解决方案: 与上面的解决方案略有不同,上面只会出现 1 或 2 次 "
但是这将一直有效,直到新行来自一行中第一次出现的 "
,然后以下内容将有所帮助,再次编写并使用显示的示例进行测试。
awk -v RS='"[^\n]*|\n+' '
RT=="\n"{ next }
[=11=]~/^msgstr/{
if(RT=="\"\""){ [=11=]=[=11=] val }
else { [=11=]=[=11=] RT }
}
[=11=]~/^msgid/ { val=RT
[=11=]=[=11=] 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.
[=12=]~/^msgstr/{ ##Checking if line starts from msgstr then:
if(RT=="\"\""){ [=12=]=[=12=] val } ##Checking if RT us "" then add val to current line.
else { [=12=]=[=12=] RT } ##Else simply add RT.
}
[=12=]~/^msgid/ { val=RT ##Checking if line starts from msgid then make val to RT
[=12=]=[=12=] RT } ##Adding RT to [=12=].
RT ##Printing line if RT is not null.
' Input_file ##Mentioning Input_file name here.
这可能适合您 (GNU sed):
sed -E 'N;s/(msgid "(.*)".*msgstr )""/""/;P;D' file
打开两行 window,如果第一行包含 msgid
和第二行 msgstr ""
,则将 msgstr
值替换为 msgid
值. Print/delete 第一行并重复。
由于输入文件的结构如此简单和一致,我认为以下内容应该足够了(它适用于您提供的 3 个示例):
sed -zE 's/(msgid "([^"]+)"\nmsgstr ")"/"/g' your_file
-z
使文件成为嵌入了\n
的一长串输入,因此我们不需要像N
、D
或其他命令,因为整个文件已经在模式 space; 中
-E
让我们可以使用(
、)
和+
而不是\(
、\)
和\+
(以及其他类似的东西)- 最外层的
()
捕获msgid "Hello"\nmsgstr "
(最后的"
匹配但未捕获); - 最里面的
()
捕获第一个双引号字符串; "
将匹配的文本(最后的"
除外)与前两个"
之间的文本和结束的"
,- 标志
g
将在整个文件中应用替换。
如果前导字符串不是那么重要(例如它们总是相同的,并且这些行总是显示为 msgid
后跟 msgstr
),您可以将上面的命令压缩更多一点:
sed -zE 's/(([^"]+)"\n[^\n]*")"/"/g' your_file
保持简单并使用 awk,例如在每个 Unix 机器上的任何 shell 中使用任何 awk:
$ awk '~/""/{=p} {p=} 1' it.po
msgid "Hello"
msgstr "Hello"
msgid "Dog"
msgstr "Cane"
如果这不是您需要的全部内容,请编辑您的问题以提供更全面的示例input/output,包括不适用的案例。
既然你有 -i
的 GNU sed,你也有或可以安装 GNU awk for -i inplace
如果你想要“就地”编辑,或者像你想要的那样做 tmp=$(mktemp) && awk 'script' file > "$tmp" && mv "$tmp" file
任何其他命令。