3

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

0001 @ fish @ animal @ eats worms

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

我怎样才能让查找和替换只发生在这一列上?

4

3 回答 3

4

你确定要使用sed吗?怎么样csvfix?您的 CSV 是否美观且简单,没有引号或嵌入的逗号或其他使正则表达式变得不那么令人满意的处理通用 CSV 文件的方式?我假设 the@是您格式中的“逗号”。

考虑使用awk而不是sed

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

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

$ 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 符号、模式(它必须在第三个字段中,因为前两个字段已经匹配)、另一个 at 符号,然后是该行的剩余部分。当该行匹配时,它用前两个字段(未更改,根据需要)替换该行,然后添加替换的第三个字段,以及该行的剩余部分(未更改,根据需要)。

如果您需要编辑而不是简单地替换第三个字段,那么您可以考虑使用awkPerl 或 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 — the @or $— 根据您是使用数组中的值还是整个数组中的值而变化。the=~是一个匹配运算符;它将 RHS 上的正则表达式应用于 LHS 上的值。替代模式识别零个或多个空格\s*pat然后是两个“单词”字符,这些字符被记住$1,然后rn和零个或多个空格;也许应该有一个^and$来绑定到字段的开头和结尾。替换是一个空格、'prefix-'、记住的一对字母、'-suffix' 和一个空格。从可能修改的单独字段中$_ = join "@", @F;重新组合输入行$_,然后将其-p打印出来。不像我想要的那样整洁(所以可能有更好的方法来做到这一点),但它确实有效。你可以毫不费力地对 Perl 中的任意字段进行任意转换。Perl 还有一个模块Text::CSV(和一个高速 C 版本Text::CSV_XS),它可以处理非常复杂的 CSV 文件。

于 2012-04-07T01:06:25.080 回答
1

基本上将线分成三部分,中间有你要找的图案。然后保留外件并更换中间件。

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

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

pattern- 你要找的东西

\([^@]*@.*\)- 在模式之后收集所有内容 - 这变成 \2

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

于 2012-04-07T01:16:43.613 回答
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. 定义要处理的字段(在本例中为第三个)并\n在它之前和之后直接插入一个换行符 ( )。s/@/&\n/2;s/@/\n&/3
  2. 将行保存在保留空间中。h
  3. 删除两边的字段s/\n@.*//;s/.*\n//
  4. 现在处理该字段,即全部更改a'sb's.y/a/b/
  5. 现在附加原始行。G
  6. 用新字段替换旧字段(同时删除任何换行符)。s/\([^\n]*\)\n\([^\n]*\).*\n/\2\1/

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

于 2012-04-07T07:19:47.667 回答