【问题标题】:kornshell -- using sed for replacementkornshell -- 使用 sed 进行替换
【发布时间】:2013-10-04 10:54:36
【问题描述】:

如果有一个文本文件包含以下内容:

1 2 3 4 5 6

如何用空格等​​字符替换某个字符串(例如 4)之前的所有内容?

1 2 3 4 5 6(输入)

    4 5 6   (OUTPUT)

【问题讨论】:

  • 如果该行是:a)1 2 3 5,b)1 2 4 5 4,c)1 2 47 3 4 5,d)1 2 34 5,输出应该是什么?有问题的字符串是否可以长于 1 个字符,例如foo?

标签: regex bash unix sed ksh


【解决方案1】:

这可能对你有用(GNU sed):

sed '/4/{:a;/^ *4/!s/[^ ]/ /;ta}' file

或:

sed 's/4/\n&/;T;h;s/[^\n]/ /g;G;s/\n.*\n//' file

或:

awk '/4/{while(!/^ *4/)sub(/[^ ]/," ")}1' file

或:

awk '/4/{sub(/4/,"\n&");h=$0;gsub(/[^\n]/," ");$0=$0 h;sub(/\n.*\n/,"")}1' file

【讨论】:

  • 我个人更喜欢你的(第二个)解决方案的简单性而不是我的性能:) 至于第一个解决方案 (':a;s/\S(\s*4)/ \1/;ta ') - 它不能在“1 2 3 4 5 4”输入上正常工作。
  • 谢谢@Vlad。我已经修改了第一个解决方案。有趣的是,我预感到这不对,但您的测试数据填补了逻辑空白。在尝试更改条件之前测试条件失败是有意义的!
【解决方案2】:

你现在真的进入了一些神秘的 sed 魔法。保持简单,只需使用清晰的函数名称、变量和可理解的结构在 awk 中编写:

$ cat file
1 2 3 4 5 6    
$ awk 'match($0,/4/){
    pre=substr($0,1,RSTART-1)
    gsub(/./," ",pre)
    print pre substr($0,RSTART)
}' file
      4 5 6

sed 是用于在单行上进行简单替换的出色工具,但对于其他任何事情,只需使用 awk。如果您使用超过 s、g 和 p(带 -n)的 sed 语言结构,那么您使用的工具是错误的。大约在 1976 年 awk 被发明后,几乎所有其他 sed 语言结构都过时了。

如果你一年后回来改进这个脚本来做一些不同的事情,例如在行尾打印一个计数,现在许多字符已被空格替换,您是否愿意尝试理解然后修改它:

sed 's/\(4\)/\n\1/;Tq;h;s/^[^\n]*\n//;x;s/\n.*//;s/./ /g;G;s/\n//;:q'

还是上面的 awk 脚本?在 awk 脚本中进行的调整只是添加和打印一个变量(下面的cnt):

awk 'match($0,/4/){
    pre=substr($0,1,RSTART-1)
    cnt=gsub(/./," ",pre)
    print pre substr($0,RSTART), cnt
}' file

我预计,要让 sed 脚本执行此操作,需要 3 只山羊、一个血月和蝙蝠侠符号。

【讨论】:

  • @anubhava 对。让我惊讶的是,人们为了强迫 sed 解决最琐碎的问题付出了多少努力。经常这样做的人告诉我,他们这样做是因为他们喜欢它,就像他们喜欢解决一个难题一样。这对他们来说很好,但它会让他们本应尝试帮助的人在他们的垃圾箱中处理难以理解的胡言乱语!
  • 在阅读了这些 a, ba, x, h H, G sed 命令的组合后,我也感到困惑:)
  • 这里的优秀点。我担心他们会被置若罔闻(至少在少数情况下)。有些人只是被 sed 保持缓冲区固定......
  • Awk 确实具有更好的可读性,sed 人只需阅读 s=substitute,h=hold 就像 awk 人阅读“gsub”/etc 一样容易。我使用 sed 主要是因为带有“正则表达式”标签的问题(perl 也适合,awk/python 不会显示太多正则表达式的使用)。此外 - 当前的 awk sn-p 删除所有没有“4”的输入行...
  • 没有 sed 人和 awk 人,就像没有螺丝刀人和锤子人一样,只有人在使用各种可用工具进行构建(在这种情况下是软件)。我使用 sed 已经 30 年了,而且我继续使用它比使用 awk 更频繁,几乎每天都使用它,因为它通常是手头简单替换工作的正确工具。如果 OP 需要,让 awk 脚本按原样打印没有 4 的行(在现有操作中分配 $0 而不是在那里打印,然后在其外部打印),这是一个微不足道的调整。
【解决方案3】:
$ echo -e '1 2 3 4 5 6\n7 8 9 0 1 2'|
> sed 's/4/\n&/;T;h;s/^[^\n]*\n//;x;s/\n.*//;s/./ /g;G;s/\n//'
      4 5 6
7 8 9 0 1 2
$

解释:

s/4/\n&/;T        # places "\n" marker before "4", quits otherwise
h;s/^[^\n]*\n//;x # puts "4 5 6" into hold space
s/\n.*//          # keeps only "1 2 3 "
s/./ /g           # replaces each char with space
G;s/\n//          # appends "4 5 6" from hold space

【讨论】:

  • 感谢您的解释!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-09
  • 1970-01-01
  • 2022-01-19
  • 2020-02-02
  • 1970-01-01
相关资源
最近更新 更多