【发布时间】:2020-07-31 18:10:04
【问题描述】:
我创建了一个非常复杂的 sed 命令来删除匹配某些模式的部分:
sed 's/...//Ig; s/...//Ig; s/...//Ig'
但我发现我犯了一个错误,我应该只编辑第一次出现:之后的部分。如何修改这个 sed 命令和/或使用其他命令来实现它?
要编辑的行实际上是 grep 的输出,例如:
/foo/bar:foobar
/foobar/foo/bar:foo_bar
【问题讨论】:
我创建了一个非常复杂的 sed 命令来删除匹配某些模式的部分:
sed 's/...//Ig; s/...//Ig; s/...//Ig'
但我发现我犯了一个错误,我应该只编辑第一次出现:之后的部分。如何修改这个 sed 命令和/或使用其他命令来实现它?
要编辑的行实际上是 grep 的输出,例如:
/foo/bar:foobar
/foobar/foo/bar:foo_bar
【问题讨论】:
awk 可以在这里作为更好的选择:
awk 'BEGIN{FS=OFS=":"} {s=$1; $1=""; gsub(/a/, "@"); gsub(/o/, "0"); print s $0}' file
/foo/bar:f00b@r
/foobar/foo/bar:f00_b@r
在这里,我们使用: 拆分输入,并将: 之前的第一个字段保存在变量s 中。然后我们运行几个gsub 函数来进行所有替换,最后我们打印保存的变量和行的其余部分。
【讨论】:
sed模式和替换可以直接在gsub函数中使用。
为了简单起见,我假设您想用FOO 替换在第一个: 之后发生的每个foo。
sed 'h # save the current line in the hold space
s/[^:]*:// # delete everything up to the marker
s/foo/FOO/g # YOUR COMPLICATED COMMAND GOES HERE
x # swap pattern and hold space
s/:.*/:/ # delete from the first : to the end in the original line
G # append hold space (: with whatever follows it)
s/\n//' yourfile # remove the newline that comes with G
以上代码是根据评论中收到的建议更新的。
原始答案如下。即使在这种情况下有点矫枉过正,但它表明您可以使用sed、\x0 中的空字符作为您通常可以假设不在文本文件中的“标记”(与使用例如 _xxx_ 可能已经在文件中)。 (第二个版本替换了foo 之前的出现 :,与我误读问题时一致。)
sed 'h # save the current line in the hold space
s/:/\x0:/ # mark the first : by prepending a null character
s/.*\x0// # delete everything up to the marker
x # swap pattern and hold space
s/:.*// # delete from the first : to the end in the original line
s/foo/FOO/g # YOUR COMPLICATED COMMAND GOES HERE
G # append hold space (: with whatever follows it)
s/\n//' yourfile # remove the newline that comes with G
【讨论】:
s/[^:]*:/:/
\x0 在很多场合都能派上用场。
.*,因为它让我认为我需要模拟一个非贪婪的*,而不是将正常/贪婪的应用到适当的令牌。
首先将grep 输出的每一行分成两行,然后在偶数行上执行sed 命令。
它看起来像
grep "something" list_of_file |
sed 's/:/\n/' |
sed '0~2s/...//Ig; 0~2s/...//Ig; 0~2s/...//Ig' |
paste -d":" - -
使用0~2,您是在告诉sed 仅对偶数行进行操作。
示例:
grep -E "root|127" /etc/{passwd,hosts} |
sed 's/:/\n/' |
sed -r '0~2s/([0,o])/==\1==/g' |
paste -d":" - -
输出:
/etc/passwd:r==o====o==t:x:==0==:==0==:r==o====o==t:/r==o====o==t:/bin/bash
/etc/hosts:127.==0==.==0==.1 l==o==calh==o==st
【讨论】:
这可能对你有用(GNU sed):
sed 's/:/\n&/;h;s/foo/FOO/g;s/bar/BAR/g;y/-/_/;H;g;s/\n.*\n//' file
在第一个 : 之前引入一个换行符。
将结果复制到保持空间 (HS)。
全局替换/翻译一次或多次。
将模式空间 (PS) 附加到 HS。
用 HS 代替 PS。
删除换行符和它们之间的所有内容。
【讨论】:
您可以尝试 Perl 替代方案。这是一种使用正面向后看的解决方案
$ cat grep_out.dat
/foo/bar:foobar
/foobar/foo/bar:foo_bar
$ perl -pe ' s/(?<=:)(foo)/\U\1/g ' grep_out.dat
/foo/bar:FOObar
/foobar/foo/bar:FOO_bar
$
【讨论】: