sed:在模式中剪切字符串

时间:2018-09-20 05:29:39

标签: sed

我有很多XHTML文件,其内容如下:

<h:panelGroup rendered="#{not accessBean.isUserLoggedIn}">
    <h:form>
        <p:panel style="margin-top:10px">
            <table style="margin:10px">
                <tbody>
                    <tr>
                        <td align="center">#{i.m['Login']}</td>
                        <td align="center">
                            <h:inputText value="#{accessBean.login}" />
                        </td>
                    </tr>
                    <tr>
                        <td align="center">#{i.m['Password']}</td>
                        <td align="center">
                            <h:inputSecret value="#{accessBean.password}" />
                        </td>
                    </tr>
                </tbody>
            </table>
            <p:commandButton ajax="false" value="#{i.m['Submit']}" action="#{accessBean.login}" />
        </p:panel>
    </h:form>
</h:panelGroup>

我想用#{i.m['any-string>']}替换每次出现的any-string,即在模式中剪切字符串。

我创建了以下sed命令

sed -e "s/#{i.m\['\(.*\)']}/\1/g"

然后在我可以执行的目录中递归运行

find . -iname '*.xhtml' -type f -exec sed -i -e "s/#{i.m\['\(.*\)']}/\1/g" {} \;

在这里,any-string可以是任何人类可读的HTML可显示字符,即字母,数字,其他字符等。这就是为什么我使用了正则表达式{{1 }}。

但它似乎无法正常工作。

以下是我使用(.*)进行的一些测试:

  1. echo

    结果:

    $ echo "<td align=\"center\">#{i.m['Login']}</td>" | sed -e "s/#{i.m\['\(.*\)']}/\1/g"
    

    确定

  2. <td align="center">Login</td>
    

    结果:

    $ echo "<p:commandButton  ajax=\"false\" value=\"#{i.m['Submit']}\" action=\"#{accessBean.login}\" />" | sed -e "s/#{i.m\['\(.*\)']}/\1/g"
    

    确定

  3. <p:commandButton  ajax="false" value="Submit" action="#{accessBean.login}" />
    

    结果:

    $ echo "<p:commandButton ajax=\"false\" value=\"#{i.m['Submit']}\" action=\"#{accessBean.login}\" /> <td align=\"center\">#{i.m['Login']}</td>" | sed -e "s/#{i.m\['\(.*\)']}/\1/g"
    

我正在使用Ubuntu 18.04。

2 个答案:

答案 0 :(得分:1)

这里的问题是您没有考虑到正则表达式的贪婪性质。您需要防止正则表达式吞噬额外的'

sed -e“ s /#{i.m ['([^'] *)']} / \ 1 / g”

这也是David C. Rankin的解决方案起作用的原因。但是,他的正则表达式不必要地复杂。

答案 1 :(得分:1)

根据您的请求,并在我的评论和其他评论中指出,您绝对应该使用xmlstartlet之类的适当XML解析器进行正确的XHTML解析。一个简单的正则表达式无法验证剩下的内容。

在您的示例中(仅举例来说),要替换保留LOGINPASSWORDSubmit的文本,您可以使用以下正则表达式:

sed "s/[#][{]i[.]m[[][']\([^']*\)['][]][}]/\1/" <file

每当您必须匹配也可以作为正则表达式本身的一部分的字符时,有助于明确确保要匹配的字符被视为字符,而不是正则表达式的一部分。为此,您要使用字符类(例如[...],其中括号之间的字符是匹配的。(如果字符类中的第一个字符是'^',将反转匹配-即匹配除类中的所有内容之外的所有内容

有了这样的解释,正则表达式应该变得很清楚。正则表达式使用基本的替换形式:

sed "s/find/replace/" file

“查找” REGEX

  • [#]-匹配井号
  • [{]-匹配左括号
  • i-匹配'i'
  • [.]-明确匹配'.'字符(而不是.任何字符)
  • m-匹配'm'
  • [[]-匹配左括号
  • [']-匹配单引号
  • \(-开始您的捕获组以捕获文本以重新插入作为后向引用
  • [^']*-匹配不是单引号的零个或多个字符
  • \)-结束捕获组
  • [']-将单引号匹配为下一个字符
  • []]-匹配右括号
  • [}]-匹配右括号。

“替换” REGEX

作为查找捕获组的一部分(在\(....\)之间)捕获的所有字符都可以用作替换的replace部分中的后向引用。 find部分中可以有多个捕获组,您在替换的替换部分将其引用为\1, \2, ...,依此类推。在这里,在find部分中只有一个捕获组,因此匹配的任何东西都可以用作整个替换,例如

  • \1-用[^']*捕获的文本替换整个混乱

使用/输出示例

要与您的示例一起使用,它将正确地保留LoginPasswordSubmit,如您的问题中所述,例如

sed "s/[#][{]i[.]m[[][']\([^']*\)['][]][}]/\1/" file
<h:panelGroup rendered="#{not accessBean.isUserLoggedIn}">
    <h:form>
        <p:panel style="margin-top:10px">
            <table style="margin:10px">
                <tbody>
                    <tr>
                        <td align="center">Login</td>
                        <td align="center">
                            <h:inputText value="#{accessBean.login}" />
                        </td>
                    </tr>
                    <tr>
                        <td align="center">Password</td>
                        <td align="center">
                            <h:inputSecret value="#{accessBean.password}" />
                        </td>
                    </tr>
                </tbody>
            </table>
            <p:commandButton ajax="false" value="Submit" action="#{accessBean.login}" />
        </p:panel>
    </h:form>
</h:panelGroup>

同样,作为免责声明和良好的常识,请勿使用正则表达式解析X / HTML,而应使用诸如xmlstartlet之类的适当工具。不要使用正则表达式来解析JSON,而是使用合适的工具来完成jq之类的工作-这样就可以避免麻烦。 (但是对于这个有限的例子,正则表达式可以很好地工作,但是它很脆弱,如果输入中的任何内容发生更改,它都会崩溃-这就是为什么我们拥有xmlstartletjq之类的工具的原因)