用“或”条件进行sed替换似乎不起作用

时间:2019-12-19 16:36:23

标签: bash sed sh

当涉及插入/替换时,我的sed正则表达式“或”似乎不起作用。给定以下数据文件,如果在第五个字段之后存在关键字,我想在关键字之前插入一个回车符。然后的想法是打印各个行。我知道Python,Perl等会更好,但是Bourne shell是必需的。

data.txt:

field1 field2 field3 field4 field5 first('echo hello') second('ls /tmp')
field1 field2 field3 field4 field5 second('ls -la /home') forth('ls /tmp')
field1 field2 field3 field4 field5 first ('touch /tmp/hello')
field1 field2 field3 field4 field5 fifth('echo hello world') first('ls /etc') third ('mkdir -p /tmp/blah')

script.sh

#!/bin/sh

while read line; do
    oldifs="$IFS"

    scriptlets=$(echo $line | cut -d ' ' -f 6- | sed -e "s=\(first|second|third|forth|fifth\)=\'$'\n\1=g")
    IFS=$'\n' # this works for Bourne shell 3.2.57
    for scriptlet in $scriptlets; do
        echo "-> $scriptlet"
    done
    IFS="$oldifs"
    echo ""

done < ./data.txt

所需的输出:

-> first('echo hello') 
-> second('ls /tmp')

-> second('ls -la /home') 
-> forth('ls /tmp')

-> first ('touch /tmp/hello')

-> fifth('echo hello world')
-> first('ls /etc')
-> third ('mkdir -p /tmp/blah')

4 个答案:

答案 0 :(得分:1)

-E下,分组括号不应反斜杠。反斜括号与字面量匹配。

此外,您对$scriptlets的赋值缺少用于命令替换的右括号。另外,您确定要两次使用命令替换,一次是在赋值中,一次是在for循环中?

最后,您的意思可能是while read line,而不是for read line,这没什么意义。

答案 1 :(得分:0)

开始时是评论,但时间太长。请参阅choroba's answer以获取适当的解决方案。

这里有很多错误。

  1. for read line无效;您可能是说while read line
  2. scriptlets=$(...缺少结束括号。
  3. $(scriptlets)可能不是您想要的-您可能是说${scriptlets}
  4. echo $line有问题。您可能想引用该变量
  5. Bash与Bourne Shell不同,尽管它兼容。例如,伯恩(Bourne)shell不支持$'...'中的C样式字符串IFS=$'\n'
  6. \n是换行符,而\r是回车符。 (这更多的是挑剔,但可能会使人们难以理解问题。)

尝试使用ShellCheck进行调试。

答案 2 :(得分:0)

显然,您不能将sed“或”与替换/插入结合在一起。因此,我需要将sed语句分解为单个语句。

postmeta

答案 3 :(得分:0)

默认情况下,sed使用“基本”正则表达式语法,该语法不支持交替(您在说的是“或”)。要使用交替,请使用sed -E和“扩展的”正则表达式语法。另外,将换行符插入替换模式的语法是乱码。尝试以下方法:

nl=$'\n'
... | sed -E $'s=(first|second|third|forth|fifth)=\\\n\\1=g' )

但实际上,我建议也以其他方式处理周围的代码。当前,它从文件中一行一行地读取行,通过cutsed一遍,收集输出,然后使用for将其分成更多行。为什么不立即将整个文件通过cutsed,然后从中拆分输出呢?同样,通常最好使用while read循环来循环行(因为它不会对shell通配符做一些愚蠢的事情)。怎么样:

#!/bin/sh

cut -d ' ' -f 6- data.txt | \
    sed -E $'s=(first|second|third|forth|fifth)=\\\n\\1=g' | \
    while read scriptlet; do
        echo "-> $scriptlet"
    done
echo 

请注意,这样做的结果是,循环在子shell中运行(因为它在管道中)。如果有问题,则需要使用bash(不是普通的sh)及其流程替换功能:

#!/bin/bash

while read scriptlet; do
    echo "-> $scriptlet"
done < <(cut -d ' ' -f 6- data.txt | \
         sed -E $'s=(first|second|third|forth|fifth)=\\\n\\1=g' )
echo