Sed 如何将查找和替换限制为CSV中的一列?

Sed 如何将查找和替换限制为CSV中的一列?,sed,awk,replace,Sed,Awk,Replace,我有一个4列CSV文件,例如: 0001 @ fish @ animal @ eats worms 我使用sed对文件进行查找和替换,但我需要将此查找和替换限制为仅在第3列中找到的文本 如何使查找和替换只发生在这一列上?您确定要使用sed?那么...怎么样您的CSV是否美观、简单,没有引号、嵌入逗号或其他使正则表达式…成为处理一般CSV文件不太令人满意的方式的污点?我假设@是您格式中的“逗号” 考虑使用awk而不是sed: awk -F@ '$3 ~ /pattern/ { OFS= "@";

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

0001 @ fish @ animal @ eats worms
我使用
sed
对文件进行查找和替换,但我需要将此查找和替换限制为仅在第3列中找到的文本


如何使查找和替换只发生在这一列上?

您确定要使用
sed
?那么...怎么样您的CSV是否美观、简单,没有引号、嵌入逗号或其他使正则表达式…成为处理一般CSV文件不太令人满意的方式的污点?我假设
@
是您格式中的“逗号”

考虑使用
awk
而不是
sed

awk -F@ '$3 ~ /pattern/ { OFS= "@"; $3 = "replace"; }'
可以说,您应该有一个BEGIN块,它可以设置一次OFS。对于一行输入,它没有任何优势(而且你可能也很难测量一百万行输入的差异):

如果
sed
仍有吸引力,则:

sed '/^\([^@]*@[^@]*\)@pattern@\(.*\)/ s//\1@replace@\2/'
例如(注意输入和输出略有不同–如果需要,您可以很容易地将其修复为与
awk
相同的处理方式):

第一个正则表达式查找一行的开始、一个非at标志字段、一个at标志字段、另一个非at标志字段,并记住该地段;它查找at符号、模式(必须在第三个字段中,因为前两个字段已经匹配)、另一个at符号,然后是行的剩余部分。当该行匹配时,它将使用前两个字段替换该行(未更改,视需要),然后添加替换的第三个字段和该行的剩余部分(未更改,视需要而定)

如果需要编辑而不是简单地替换第三个字段,那么可以考虑使用
awk
或Perl或Python。如果您仍被限制为
sed
,则在处理图案空间中的另一部分时,您可以探索使用保持空间来保持线条的一部分,并最终在打印线条之前从保持空间和图案空间重新整合所需的输出线条。这几乎和听起来一样混乱;事实上,可能比听起来更混乱。我会选择Perl(因为我很久以前就学会了,而且它很容易做到这一点),但是您可以使用任何您喜欢的非sed工具


Perl编辑第三个字段。请注意,默认输出是
$\uuuz
,它必须从数组
@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
表示“循环,将行读入
$\u
并在每次迭代结束时打印
$\u
”。
-a
表示“自动将
$分割到数组
@F
”中。
-F@
表示字段分隔符为
@
-e
后面跟着Perl程序。在Perl中,数组是从0索引的,因此第三个字段被拆分为
$F[2]
(sigil-the
@
$
-根据您使用的是数组中的值还是整个数组中的值而变化。
=
是一个匹配运算符;它将RHS上的正则表达式应用于LHS上的值。替换模式识别零个或多个空格
\s*
,后跟
pat然后将两个“word”字符存储到
$1
,然后再存储到
rn
和零个或多个空格中;可能其中应该有一个
^
$
,以绑定到字段的开头和结尾。替换的是一个空格、'prefix-',记住的一对字母和“-suffix”以及一个空格。
$\=join“@”,@F;
从可能修改的单独字段重新组装输入行
$\ucode>,然后
-p
打印出来。没有我想要的那么整洁(因此可能有更好的方法),但它可以工作。而且您可以在Perl中对任意字段进行任意转换,而不会有太多困难。Perl还有一个模块
Text::CSV
(以及一个高速C版本
Text::csvxs
)它可以处理复杂的CSV文件。

基本上把线分成三块,中间要找的图案,然后保持外片并替换中间。

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

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

模式
-您正在寻找的东西

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


然后将该行更改为
\1
,然后是
替换
,然后是
模式
之后的所有内容,即
\2

这可能适用于您:

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
说明:

  • 定义要处理的字段(在本例中为第3个),并在其前面和后面直接插入一个换行符(
    \n
    )。
    s/&\n/2;s//\n&/3
  • 将该行保存在保留空间。
    h
  • 删除
    s两侧的字段/\n@.*//;s/*\n/
  • 现在处理该字段,即将所有
    a
    更改为
    b
    y/a/b/
  • 现在添加原始行。
    G
  • 用新字段替换旧字段(同时删除任何换行)。
    s/\([^\n]*\)\n\([^\n]*\).\n/\2\1/

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

    这不是CSV,但我认为您使我们更易于阅读。是否可以保证字段之间的分隔符(在您的示例中显示为@)正确永远不会出现在其他任何地方?因此,您是在问如何查找第二个和第三个'@'之间的文字,还是更复杂,即模式?请举例说明需要查找的内容,以及需要查找的内容
    $ 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
    $
    
    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