我有一个4列的CSV文件,例如:
0001 @ fish @ animal @ eats worms
我使用sed
对文件进行查找和替换,但我需要将此查找限制并仅替换为第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
说明:
\n
)。 s/@/&\n/2;s/@/\n&/3
h
s/\n@.*//;s/.*\n//
a's
更改为b's
。 y/a/b/
G
s/\([^\n]*\)\n\([^\n]*\).*\n/\2\1/
N.B。在步骤4中,模式空间仅包含已定义的字段,因此可以在此处执行任意数量的命令,结果不会影响该行的其余部分。