我正在编写一个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/<awk command 1>/<awk command 2>/' myHtmlFile
不幸的是,我花了几个小时以各种方式改变了这一点,没有运气。
似乎存在语法问题:
sed "s#$(awk -F': ' '{print $1}' testfile)#$(awk -F': ' '{print $1}' testfile | awk -F'\' '{print $NF}')#" testfile
是否使用单引号或双引号,无论是尝试串联sed字符串还是根据选定的语法尝试转义各种字符。
我虽然也关于循环(这样我才能使sed与包含awk结果的var一起工作),但是我不确定如何为这种内联编辑管理循环。 可以使用功能强大的正则表达式解决此问题,但我对此很不好^^
答案 0 :(得分:0)
$ 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>
上面的正则表达式可以做到这一点:
[^:]+/
-使用不包含冒号且以/
结尾的前导字符串,例如/path/to/
([^:!]+)
-将不包含冒号或感叹号的后续字符串保存在捕获组中,例如infected-zipfile
.*:
-消耗随后的字符串,导致冒号,后跟一个空白字符,例如!(1)ZIP:eicar.com:
。然后替换执行此操作:
\1
-打印上面步骤2中保存到捕获组1中的字符串:
-打印冒号,后跟空白字符(我也可以使用捕获组)答案 1 :(得分:0)
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
实际上,您可以跳过fullpath
和trimmedpath
变量,并根据需要直接在sed命令中使用两个$(echo "$line" | awk...)
替换。但是,实际上,您根本不应该这样做。使用Ed的单正则表达式解决方案。
答案 2 :(得分:0)
这可能对您有用(GNU sed):
sed -r 's#^([^/]*/)*([[:alpha:]-]*)([^:]*:)* #\2: #' file
这将删除所有目录,保留文件名,并删除所有多余的字符,直到:
后跟一个空格。