SED错误地仅替换一行上的第一个模式实例

时间:2014-09-09 19:42:10

标签: bash sed

您好:我有表格的分页数据 客户项目描述 - 购买价格 - 类别

e.g。 a.out包含:

1\t400 Bananas\t3.00\tfruit
2\t60 Oranges\t0.00\tfruit
3\tNULL\t3.0\tfruit
4\tCarrots\tNULL\tfruit
5\tNULL\tNULL\tfruit

我试图摆脱所有的NULL字段。我不能依赖于字符串“NULL”的简单替换,因为它可能是一个子字符串;所以我正在尝试

sed -i 's:\tNULL\t:\t\t:g' a.out 

当我这样做时,我最终会

1\t400 Bananas\t3.00\tfruit
2\t60 Oranges\t0.00\tfruit
3\t\t3.0\tfruit
4\tCarrots\t\tfruit
5.\t\tNULL\tfruit

这里的错误是#5只在每一行上替换了第一个搜索字符串实例。

如果我两次运行我的sed命令,我最终会得到我想要的结果:

1\t400 Bananas\t3.00\tfruit
2\t60 Oranges\t0.00\tfruit
3\t\t3.0\tfruit
4\tCarrots\t\tfruit
5.\t\t\tfruit

您可以看到第5行删除了两个NULL 但是我不明白我为什么要这么痛苦?

4 个答案:

答案 0 :(得分:3)

由于标签不能出现在您的案例中的字符串内,因为这意味着您可以通过执行此操作来实现您想要的新字段;

sed ':start ; s/\tNULL\(\t\|$\)/\t\1/ ; t start' a.out

首先,内部s/\tNULL\(\t\|$\)/\t\1/搜索tab NULL,然后搜索tab或行尾$,并替换为tab然后是NULL之后出现的字符(最后一部分是使用\1完成的)。我们称之为expression

我们现在有:

sed ':start ; expression ; t start' a.out

这实际上是一个循环(如goto)。 :start是一个标签。 ;充当语句分隔符。我已经描述了上面的表达式。 t start表示,如果表达式做了任何替换,则会跳转到标签start。缓冲区将包含替换文本。发生此循环直到无法在该行上进行替换,然后继续处理。

有关sed流量控制和其他有用花絮的信息可以找到here

答案 1 :(得分:3)

awk -F'\t' -v OFS='\t' '{
    for (i = 1; i <= NF; ++i) {
        if ($i == "NULL") {
            $i = "";
        }
    }
    print
}' test.txt

直接的解决方案是使用\t作为字段分隔符,然后遍历所有字段以查找"NULL"的完全匹配。没有子串。

这里和一个班轮一样:

awk -F'\t' -v OFS='\t' '{for(i=1;i<=NF;++i) if($i=="NULL") $i=""} 1' test.txt

答案 2 :(得分:0)

awk简化了:

awk -F '\tNULL\\>' -v OFS='\t' '{$1=$1}1' file
1\t400 Bananas\t3.00\tfruit
2\t60 Oranges\t0.00\tfruit
3\t\t3.0\tfruit
4\tCarrots\t\tfruit
5\t\t\tfruit

答案 3 :(得分:0)

来自最近的Linux上的grep(1)

反斜杠字符和特殊表达

符号\&lt;和&gt;分别匹配空字符串 一个词的开头和结尾。符号\ b与空字符串匹配 一个词的边缘[...]

-

那么,怎么样:

sed -i 's:\<NULL\>::g' a.out