Bash - 在文件中搜索特定字符串并替换为直接源

Bash - Search for a specific string in a file and replace with immediate source

我正在尝试在包含以下内容的文件中搜索字符串 "missing":

<message>
    <source>TypeA</source>
    <translation>missing</translation>
</message>
<message>
    <source>TypeB</source>
    <translation>missing</translation>
</message>
<message>
    <source>TypeC</source>
    <comment>Context menu</comment>
    <translation>missing</translation>
</message>

如果找到 "missing",我想用它的直接源名称替换该字符串。例如:

<message>
    <source>TypeA</source>
    <translation>TypeA</translation>
</message>
<message>
    <source>TypeB</source>
    <translation>TypeB</translation>
</message>
<message>
    <source>TypeC</source>
    <comment>Context menu</comment>
    <translation>TypeC</translation>
</message>

到目前为止,我能够使用 awk 搜索字符串并打印直接源名称:

match([=13=], /<source>(.*)<\/source>/,n){ src=n[1] }
match([=13=], /<translation>(.*)<\/translation>/,s){ trs=s[1] }
/unfinished/{ print "Translation missing or incomplete for: '" trs "'","located inside source named: '" src "'" }

然后将其保存为 something.awk 使用以下方式调用它:

awk -f something.awk filelocation

但我不确定如何用源中的值替换字符串 "missing"。

任何人都可以建议我如何替换它吗?

你可以试试这个(写在something.awk):

{
    if([=10=] ~ "<source>"){
            source = gensub(/.*<source>(.*)<\/source>.*/, "\1", "", [=10=])
    }
    if([=10=] ~ "<translation>missing"){
            [=10=] = gensub(/>.*</, ">" source "<", "", [=10=])
    }
     print
}

我不知道您是否需要特定版本的 awk 才能使用 gensub...(也许是 gawk?)。但是当我这样做时它可以在我的电脑上运行:

awk -f something.awk filelocation

结果:

<message>
    <source>TypeA</source>
    <translation>TypeA</translation>
</message>
<message>
    <source>TypeB</source>
    <translation>TypeB</translation>
</message>
<message>
    <source>TypeC</source>
    <comment>Context menu</comment>
    <translation>TypeC</translation>
</message>

正如我所说,如果不遵守标签的顺序(或者如果每行有多个标签,...),这可能是一个严重的问题。如果您在源和翻译之间有一个标签,这没什么大不了的,但源必须在翻译之前。如果不是这种情况,您可能需要使用正确的 XML 解析器工具(awk iksn't)解析您的文件,然后进行更改并打印到文件。

虽然你已经接受了答案,但我会添加这个。

在您的评论中,您告诉我们您的输入文件是格式正确的 xml 文档。所以我会以 xml 的方式处理它。我喜欢 awk/sed/grep,但我不得不说它们(和正则表达式)确实不是处理 xml 文件的正确工具,尽管它有时工作起来又快又脏。

有命令行工具:xsltproc,可以通过xslt将xml文档转换成其他格式。

xslt也比较简单:(另存为f.xslt

<?xml version="1.0"?>
<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:template match="node()|@*">
        <xsl:copy>
            <xsl:apply-templates select="node()|@*"/>
        </xsl:copy>
    </xsl:template>
    <xsl:template match="translation[.='missing']">
        <xsl:copy>
            <xsl:value-of select="../source"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

你需要做的只是:

xsltproc f.xslt input.xml

用你的输入文件测试一下:(我添加了一个根元素

kent$  cat f.xml
<root>
        <message>
                <source>TypeA</source>
                <translation>missing</translation>
        </message>
        <message>
                <source>TypeB</source>
                <translation>missing</translation>
        </message>
        <message>
                <source>TypeC</source>
                <comment>Context menu</comment>
                <translation>missing</translation>
        </message>
</root>

kent$  xsltproc f.xslt f.xml
<?xml version="1.0"?>
<root>
        <message>
                <source>TypeA</source>
                <translation>TypeA</translation>
        </message>
        <message>
                <source>TypeB</source>
                <translation>TypeB</translation>
        </message>
        <message>
                <source>TypeC</source>
                <comment>Context menu</comment>
                <translation>TypeC</translation>
        </message>
</root>

只要您的输入 xml 格式正确,这将始终有效。即使你在一行中输入 xml 或其他格式也可以。

您可以将输出用作变量并将其传递给 sed 进行替换,在我看来,简单使它变得完美,

sed s/'$AWK_RESULT/"REPALACE_STATE"/g'

在迭代孔文本期间,您也可以在循环中使用它。