当涉及插入/替换时,我的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')
答案 0 :(得分:1)
在-E
下,分组括号不应反斜杠。反斜括号与字面量匹配。
此外,您对$scriptlets
的赋值缺少用于命令替换的右括号。另外,您确定要两次使用命令替换,一次是在赋值中,一次是在for
循环中?
最后,您的意思可能是while read line
,而不是for read line
,这没什么意义。
答案 1 :(得分:0)
开始时是评论,但时间太长。请参阅choroba's answer以获取适当的解决方案。
这里有很多错误。
for read line
无效;您可能是说while read line
。scriptlets=$(...
缺少结束括号。$(scriptlets)
可能不是您想要的-您可能是说${scriptlets}
echo $line
有问题。您可能想引用该变量$'...'
中的C样式字符串IFS=$'\n'
。\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' )
但实际上,我建议也以其他方式处理周围的代码。当前,它从文件中一行一行地读取行,通过cut
和sed
一遍,收集输出,然后使用for
将其分成更多行。为什么不立即将整个文件通过cut
和sed
,然后从中拆分输出呢?同样,通常最好使用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