【问题标题】:sed -i '/$(command 1)/$(command 2)/' myHtmlFile ? Inline editing with sed and awksed -i '/$(command 1)/$(command 2)/' myHtmlFile ?使用 sed 和 awk 进行内联编辑
【发布时间】:2018-12-28 03:59:36
【问题描述】:

我正在编写一个 shell 脚本,用于构建和编辑一个 html 文件,该文件的主要内容基本上是 clamscan (ClamAV) 的输出。 所以,脚本的任务是:翻译输出,删除无用的东西,添加 html 标签等等。 不过,我坚持我想要的最后一个修改。

clamscan 的编辑输出的一部分如下所示:

/path/to/infected-file: Eicar-Test-Signature<span class="mep-subhead-warning"> FOUND</span>
/path/to/infected-zipfile!(1)ZIP:eicar.com: Eicar-Test-Signature<span class="mep-subhead-warning"> FOUND</span>
/path/to/infected-zipfilewithinzipfile!ZIP:eicar_com.zip!(2)ZIP:eicar.com: Eicar-Test-Signature<span class="mep-subhead-warning"> FOUND</span>

我想缩小那些长线。像这样的东西是最好的:

infected-file: Eicar-Test-Signature<span class="mep-subhead-warning"> FOUND</span>
infected-zipfile: Eicar-Test-Signature<span class="mep-subhead-warning"> FOUND</span>
infected-zipfilewithinzipfile: Eicar-Test-Signature<span class="mep-subhead-warning"> FOUND</span>

但我已经很乐意删除受感染文件的路径。

由于使用 awk 似乎很容易获得一些结果,并且我在之前的所有编辑中都使用了 sed,所以我认为我最好的选择是: sed -i 's/&lt;awk command 1&gt;/&lt;awk command 2&gt;/' myHtmlFile

不幸的是,我花了几个小时以各种方式改变它,但没有运气。

似乎存在语法问题,例如:

sed "s#$(awk -F': ' '{print $1}' testfile)#$(awk -F': ' '{print $1}' testfile | awk -F'\' '{print $NF}')#" testfile

我是使用单引号还是双引号,是尝试连接 sed 字符串还是尝试根据所选语法转义各种字符。

我也考虑过循环(这样我就可以使 sed 与包含 awk 结果的变量一起工作),但我不确定如何为这种内联编辑管理循环。 它可能可以用一个强大的正则表达式来解决,但我很不擅长它^^

【问题讨论】:

    标签: regex bash awk sed


    【解决方案1】:
    $ sed -E 's#[^:]+/([^:!]+).*: #\1: #' file
    infected-file: Eicar-Test-Signature<span class="mep-subhead-warning"> FOUND</span>
    infected-zipfile: Eicar-Test-Signature<span class="mep-subhead-warning"> FOUND</span>
    infected-zipfilewithinzipfile: Eicar-Test-Signature<span class="mep-subhead-warning"> FOUND</span>
    

    上面的正则表达式是这样做的:

    1. [^:]+/ - 使用不包含冒号并以 / 结尾的前导字符串,例如/path/to/
    2. ([^:!]+) - 在捕获组中保存不包含冒号或感叹号的后续字符串,例如infected-zipfile
    3. .*: - 使用以冒号开头的后续字符串,后跟一个空白字符,例如!(1)ZIP:eicar.com:

    然后替换会这样做:

    1. \1 - 打印上面第 2 步中保存到捕获组 1 中的字符串
    2. : - 打印一个冒号后跟一个空白字符(我也可以为此使用捕获组)

    【讨论】:

    • 完美。我完全不明白正则表达式 ^^'
    • 好的,我加了解释。
    • 非常感谢!因此,要进行内联编辑,我在 bash 脚本中插入的是:sed -i -r '/mep-subhead-warning/s#[^:]+/([^:!]+).*: #\1: #' myHtmlFile
    【解决方案2】:

    Ed Morton 已经解释了如何使用单个正则表达式替换(即正确的方法)来做到这一点;我将解释原始方法有什么问题,并展示如何使用 shell 循环(即错误的方法)。

    组合 sed+awk+awk 方法的问题是您需要它们以锁步方式逐行操作。也就是说,当 sed 处理文件的第 N 行时,它需要将第一个 awk 命令的第 N 行输出替换为第二个 awk 管道的第 N 行输出。但是这些命令并不是这样相互关联的。 shell 运行所有 awk 命令,收集它们的全部输出,然后将其作为一个巨大的(且格式错误的)替代表达式提供给 sed。鉴于您的示例数据(并假设最后一个 awk 命令应该有 -f '/' 而不是 -f '\'),它基本上会这样做:

    sed 's#/path/to/infected-file
    /path/to/infected-zipfile!(1)ZIP:eicar.com
    /path/to/infected-zipfilewithinzipfile!ZIP:eicar_com.zip!(2)ZIP:eicar.com#infected-file
    infected-zipfile!(1)ZIP:eicar.com
    infected-zipfilewithinzipfile!ZIP:eicar_com.zip!(2)ZIP:eicar.com#' testfile
    

    sed 会因为模式中的换行符(以及替换字符串)而拒绝此操作。如果不是因为它被拒绝,sed 会继续尝试将整个混乱分别应用于每一行,但因为它实际上不是你想要的,所以它也不起作用。

    为了让所有这些命令以锁步方式逐行运行,您必须执行一些操作,例如使用 shell 循环通过每个命令(&pipeline)单独读取和处理每一行,例如这个:

    while read -r line; do
        fullpath=$(echo "$line" | awk -F': ' '{print $1}')
        trimmedpath=$(echo "$line" | awk -F': ' '{print $1}' testfile | awk -F'/' '{print $NF}'
        echo "$line" | sed "s#$fullpath#$trimmedpath#"
    done < testfile
    

    您实际上可以跳过 fullpathtrimmedpath 变量,如果需要,直接在 sed 命令中使用两个 $(echo "$line" | awk...) 替换。但实际上,您根本不应该这样做。使用 Ed 的单正则表达式解决方案。

    【讨论】:

      【解决方案3】:

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

      sed -r 's#^([^/]*/)*([[:alpha:]-]*)([^:]*:)* #\2: #' file
      

      这将删除所有目录,保留文件名并删除任何多余的字符,直到 : 后跟一个空格。

      【讨论】:

        猜你喜欢
        • 2012-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-12-02
        • 1970-01-01
        • 2017-09-19
        相关资源
        最近更新 更多