如何限制查找和替换CSV中的只有一列?

时间:2012-04-07 00:46:14

标签: sed awk replace

我有一个4列的CSV文件,例如:

0001 @ fish @ animal @ eats worms

我使用sed对文件进行查找和替换,但我需要将此查找限制并仅替换为第3列中的文本。

如何只在这一列上进行查找和替换?

3 个答案:

答案 0 :(得分:4)

您确定要使用sed吗?那么csvfix呢?你的CSV好看又简单,没有引号或嵌入式逗号或其他制作正则表达式的恶作剧......处理一般CSV文件的方式不尽如人意?我假设@是您格式的'逗号'。

考虑使用awk代替sed

awk -F@ '$3 ~ /pattern/ { OFS= "@"; $3 = "replace"; }'

可以说,你应该有一个BEGIN块来设置OFS一次。对于一行输入,它没有任何可能性(你也可能很难衡量一百万行输入的差异):

$ echo "pattern @ pattern @ pattern @ pattern" | 
> awk -F@ '$3 ~ /pattern/ { OFS= "@"; $3 = "replace"; }'
pattern @ pattern @replace@ pattern
$

如果sed仍然具有吸引力,那么:

sed '/^\([^@]*@[^@]*\)@pattern@\(.*\)/ s//\1@replace@\2/'

例如(并注意稍微不同的输入和输出 - 如果需要,您可以将其修复为与awk 完全相同处理):

$ echo "pattern@pattern@pattern@pattern" |
> sed '/^\([^@]*@[^@]*\)@pattern@\(.*\)/ s//\1@replace@\2/'
pattern@pattern@replace@pattern
$

第一个正则表达式寻找一条线的起点,一个非符号的字段,一个at-sign,另一个非符号的字段并记住该批;它寻找一个符号,模式(必须在第三个字段中,因为前两个字段已经匹配),另一个符号,然后是行的残差。当该行匹配时,它将用前两个字段替换该行(根据需要保持不变),然后添加替换的第三个字段和行的剩余部分(根据需要不变)。

如果您需要编辑而不是简单地替换第三个字段,那么您可以考虑使用awk或Perl或Python。如果仍然被约束到sed,那么在操作模式空间中的其他部分时,可以使用保持空间来保持线的一部分,并最终从保持空间重新集成所需的输出线和打印线之前的图案空间。这几乎和听起来一样混乱;实际上,甚至可能比听起来更混乱。我会选择Perl(因为我很久以前就已经学会了它并且很容易做到这一点),但你可以使用你喜欢的非sed工具。


Perl编辑第三个字段。请注意,默认输出为$_,必须从数组@F中的自动拆分字段重新组合。

$ echo "pattern@pattern@pattern@pattern" | sh -x xxx.pl
> perl -pa -F@ -e '$F[2] =~ s/\s*pat(\w\w)rn\s*/ prefix-$1-suffix /; $_ = join "@", @F; ' "$@"
pattern@pattern@ prefix-te-suffix @pattern
$

解释。 -p表示'循环,在每次迭代结束时读取行$_并打印$_'。 -a表示“自动拆分$_到数组@F”。 -F@表示字段分隔符为@-e之后是Perl程序。数组在Perl中从0开始索引,因此第三个字段被拆分为$F[2](sigil - @$ - 更改取决于您是否使用了来自数组或数组作为一个整体。=~是匹配运算符;它将RHS上的正则表达式应用于LHS上的值。替换模式识别零个或多个空格\s*后跟{{ 1}}然后将两个“单词”字符记住到pat,然后再记为$1,再次出现零个或多个空格;也许应该有一个rn^绑定到字段的开头和结尾。替换是一个空格,'前缀 - ',记住的字母对,'-suffix'和空格。$重新组合输入行{{1从可能修改的单独字段开始,然后$_ = join "@", @F;打印出来。不像我想的那样整洁(所以可能有更好的方法),但是它有效。你可以做Perl中任意字段的任意变换没有太大困难.Perl也有一个模块{{ 1}}(以及一个高速C版本$_)可以处理非常复杂的CSV文件。

答案 1 :(得分:1)

基本上将线条分成三个部分,中间是你正在寻找的模式。然后保留外部碎片并更换中间部分。

/\([^@]*@[^@]*@\[^@]*\)pattern\([^@]*@.*\)/s//\1replacement\2/

\([^@]*@[^@]*@\[^@]*\) - 收集模式之前的所有内容,包括第3个@和数学前的任何文本 - 这将成为\ 1

pattern - 您正在寻找的东西

\([^@]*@.*\) - 收集模式后的所有内容 - 这将成为\ 2

然后将该行更改为\1,然后更改为replacement,然后将pattern后的所有内容更改为\2

答案 2 :(得分:1)

这可能对您有用:

echo 0001 @ fish @ animal @ eats worms|
sed 's/@/&\n/2;s/@/\n&/3;h;s/\n@.*//;s/.*\n//;y/a/b/;G;s/\([^\n]*\)\n\([^\n]*\).*\n/\2\1/'
0001 @ fish @ bnimbl @ eats worms

说明:

  1. 定义要处理的字段(在本例中为第3个)并在其前面和后面直接插入换行符(\n)。 s/@/&\n/2;s/@/\n&/3
  2. 将该行保存在保留空间中。 h
  3. 删除s/\n@.*//;s/.*\n//
  4. 旁边的字段
  5. 现在处理该字段,即将所有a's更改为b'sy/a/b/
  6. 现在附加原始行。 G
  7. 用旧字段替换新字段(也删除任何换行符)。 s/\([^\n]*\)\n\([^\n]*\).*\n/\2\1/
  8. N.B。在步骤4中,模式空间仅包含已定义的字段,因此可以在此处执行任意数量的命令,结果不会影响该行的其余部分。