2012-04-07 21 views
3

我有一个4列的CSV文件,如:如何限制查找并替换为CSV中的一列?

0001 @ fish @ animal @ eats worms 

我用sed做一个查找和对文件替换,但我需要限制这个查找和替换只内第3列找到的文本。

如何在这一列上找到并替换?

+0

这不是一个CSV,但我认为你让我们更容易阅读。是否保证字段之间的分隔符(在你的例子中显示为@)可以**从不**出现在其他地方?那么你是问如何在第二个和第三个'@'之间寻找一段文字,还是比较复杂,即模式?请举例说明需要找到什么,以及它将被替代的内容。 – gbulmer 2012-04-07 00:53:54

+0

它是一个CSV文件,但使用'@'将列与周围空格分开。我只显示一行显示格式。逗号在整个文件中频繁出现,但是'@'从不出现,所以我用'@'作为分隔符。一个替换例子是'sed -i“s/a/b/g”./file.csv'(用“b”替换所有出现的“a”,除了我只希望这个替换出现在列中的条目内3,并且不会影响'@'两边的空格。 – Village 2012-04-07 01:03:41

回答

4

您确定要使用sed?那么csvfix?您的CSV文件是否简洁明了,没有引号或嵌入逗号或其他使正则表达式出现的问题......处理一般CSV文件的方式不尽人意。我假设@是您格式中的'逗号'。

考虑使用awk代替sed

awk [email protected] '$3 ~ /pattern/ { OFS= "@"; $3 = "replace"; }' 

按理说,你应该有一个BEGIN块,设置OFS一次。对于输入的一条线,它没有做任何可能性(和你可能会捉襟见肘衡量上万行输入的差别,太):

$ echo "pattern @ pattern @ pattern @ pattern" | 
> awk [email protected] '$3 ~ /pattern/ { OFS= "@"; $3 = "replace"; }' 
pattern @ pattern @[email protected] pattern 
$ 

如果sed似乎仍然有吸引力,然后:

sed '/^\([^@]*@[^@]*\)@[email protected]\(.*\)/ s//\[email protected]@\2/' 

例如(并注意略有不同的输入和输出–您可以修复它来处理一样awk很容易如果需要的话):

$ echo "[email protected]@[email protected]" | 
> sed '/^\([^@]*@[^@]*\)@[email protected]\(.*\)/ s//\[email protected]@\2/' 
[email protected]@[email protected] 
$ 

第一个正则表达式寻找一行的开始,一个非符号字段,一个at-sign字段,另一个非符号字段并记住这个字段;它寻找一个符号,模式(它必须在第一个两个字段已匹配的第三个字段中),另一个符号,然后是行的余数。当线条匹配时,它会用前两个字段替换线条(根据需要不变),然后添加替换的第三个字段和线条的剩余部分(根据需要不变)。

如果您需要编辑而不是简单地替换第三个字段,那么您考虑使用awk或Perl或Python。如果您仍然受限于sed,那么您可以探索使用保持空间来保持部分行,同时在模式空间中操作其他部分,并最终从保留空间和模式空间重新整合所需的输出行打印该行。这听起来很麻烦,实际上,甚至可能比听起来更混乱。我会和Perl一起去​​(因为我很久以前就学会了它,它很容易做到这一点),但是你可以使用你喜欢的任何非sed工具。


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

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

解释。-p意味着'循环,在每次迭代结束时将行读入$_并打印$_'。-a的意思是'自动拆分$_到数组@F'。 [email protected]表示字段分隔符是@-e后面跟着Perl程序。数组从Perl中的索引0开始,因此第三个字段被拆分为$F[2](印记— @$ —的变化取决于您是使用阵列中的某个值还是整个数组。=~是一个匹配运算符;它将RHS上的正则表达式应用于LHS上的值,替代模式识别零个或多个空格\s*,然后是pat,然后将两个“单词”字符记住为$1,然后rn并再次为零或多个空格;也许应该有一个^$在那里绑定到字段的开始和结束,替换是一个空格,'prefix-',记住的一对字母,' - suffix'和一个空格$_ = join "@", @F;从可能修改的单独字段重新组装输入行$_,然后-p将其打印出来。没有我想要的那么整齐(所以可能有更好的方法来做到这一点),但它的工作原理。您可以在Perl中的任意字段上进行任意转换,而不会有太大困难。 Perl还有一个模块Text::CSV(和一个高速C版本,Text::CSV_XS),它可以处理非常复杂的CSV文件。

1

基本上把这条线分成三块,用你在中间寻找的图案。然后保持外部件,并更换中间。

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

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

pattern - 你正在寻找

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

然后将该行改为\1,然后replacement,那么一切pattern后,这是\2

1

这可能会为你工作:

echo 0001 @ fish @ animal @ eats worms| 
sed 's/@/&\n/2;s/@/\n&/3;h;s/\[email protected]*//;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/\[email protected]*//;s/.*\n//
  4. 现在处理字段即改变所有a'sb'sy/a/b/
  5. 现在附加原始行。 G
  6. 替换旧字段的新字段(也删除任何换行符)。 s/\([^\n]*\)\n\([^\n]*\).*\n/\2\1/

N.B.在步骤4中,模式空间仅包含定义的字段,因此可以在此处执行任意数量的命令,并且结果不会影响该行的其余部分。