我有一个CSV文件,它使用高度自定义的格式。这里,每个数字代表4列中每一列的数据:
1 2 [3] 4
我需要将sed
限制为仅搜索和修改第四列中显示的数据。从本质上讲,它必须忽略在第一次出现结束方括号和空格]
之前出现的行上的所有数据,并且只修改之后出现的数据。例如,file1.txt
可能包含:
penguin bird [lives in Antarctica] The penguin lives in cold places.
wolf dog [lives in Antarctica with penguins] The wolf likes to eat penguins.
替换可能是sed 's/penguin/animal/g' file1.txt
。运行脚本后,输出将如下所示:
penguin bird [lives in Antarctica] The animal lives in cold places.
wolf dog [lives in Antarctica with penguins] The wolf likes to eat animal.
在这种情况下,penguin
的所有外观都会在第一个]
之前被忽略,并且只会在之后出现的行上更改。
如何sed
在找到并替换文字时忽略此自定义CSV格式的前三列?
我有GNU sed版本4.2.1。
答案 0 :(得分:3)
你告诉sed搜索']'组合后跟.*
(任何东西),然后作为替换的一部分,你放回]
个字符。
唯一的问题是sed
通常“认为”]
char是字符类定义的一部分,所以你必须逃避它。尝试
echo "a b [c] d" | sed 's/\] .*$/\] XYZ/'
a b [c] XYZ
注意,因为没有开放[
字符来表示char-class def,所以你可以使用
echo "a b [c] d" | sed 's/] .*$/] XYZ/'
a b [c] XYZ
修改强>
要修正第4个单词,
echo "a b [c] d e" | sed 's/\] [^ ][^ ]*/\] XYZ/'
a b [c] XYZ e
从上面[^ ][^ ]/
添加“any-char-that-not-a-space”后跟任意数量的“any-char-that-not-a-space”,所以当匹配器发现下一个空格停止匹配时。
最终修改
echo "penguin bird [lives in Antarctica] The penguin lives in cold places.
wold dog [lives in Antarctica with penguins] The wolf likes to eat penguins." \
| sed 's/\] The penguin \(.*$\)/] The animal \1/'
当你使用gnu sed时,你不需要逃避(...
捕获的parens。
echo "penguin bird [lives in Antarctica] The penguin lives in cold places.
wold dog [lives in Antarctica with penguins] The wolf likes to eat penguins." \
| sed 's/\] The penguin (*$)/] The animal \1/'
<强>输出强>
penguin bird [lives in Antarctica] The animal lives in cold places.
wolf dog [lives in Antarctica with penguins] The wolf likes to eat penguins.
取决于您使用的sed版本。 sed
AIX
与solaris
之间存在相当大的差异,VS通常在lunix中找到的GNU seds。
如果您对使用sed有其他疑问,通常可以添加sed --version
或sed -V
的输出。如果没有来自这些命令的响应,请尝试what sed
。否则包括uname
的操作系统名称。
IHTH
答案 1 :(得分:2)
假设您只有一次结束括号,我会使用awk
来执行此操作:
awk 'BEGIN {FS=OFS="]"} { gsub(/penguin/, "animal", $2) }1' file.txt
结果:
penguin bird [lives in Antarctica] The animal lives in cold places.
wolf dog [lives in Antarctica with penguins] The wolf likes to eat animals.
答案 2 :(得分:2)
通常我会像shelter所描述的那样(如果我只是输入一个快速sed
命令行),但它的缺点是,一旦你开始匹配部分输入以保留它(使用{ {1}}等)您必须匹配并替换所有内容,并且不能再使用\1
之类的简单替换。如果你愿意添加一些样板围绕替换,你可以在保留缓冲区中隐藏行的开头,然后将其取回:
s/penguin/animal/
sed -e 'h' \
-e 's/.*\] //' \
-e 's/penguin/animal/' \
-e 'x' \
-e 's/\] .*/] /' \
-e 'G' \
-e 's/\n//'
将原始行保存在保留空间中。然后我们删除前缀并在行的末尾进行任何替换(在此处选择示例)或一系列替换。然后h
交换结束和保存的副本。我们从保存的副本中删除原始结尾,并使用x
将它们重新组合在一起。 G
添加了我们不想要的换行符,因此我们将其删除。
答案 3 :(得分:1)
这可能适合你(GNU sed);
sed -i 's/\]/&\n/;h;s/.*\n//;s/penguin/animal/g;H;g;s/\n.*.\n//' file
说明:
s/\]/&\n/
使用\n
标记分割h
复制该行s/.*\n//
删除您不想更改的部分s/penguin/animal/g
更改您要更改的部分H;g
将其添加回原始行s/\n.*\n//
删除您要更改的原始行的部分这适用于每一行,如果更改是有条件的,请使用:
sed -i '/\]/!b;s//&\n/;h;s/.*\n//;s/penguin/animal/g;H;g;s/\n.*.\n//' file
另一种选择(也许更简单的方法):
sed ':a;s/\(\].*\)penguin/\1animal/;ta' file