【问题标题】:First occurrence on every line vi/vim/sed etc每行第一次出现 vi/vim/sed 等
【发布时间】:2012-06-16 20:25:52
【问题描述】:

使用 VI 替换第一个匹配项/实例非常简单。

    :%s/search/replace/args

但是,这是我的 .csv 格式/文件的数据集:

"192.168.2.1","www.google.com","2009/01/11_10:00"," What a great website"
"192.168.2.2/driving/is/fun","-","2009/03/22_00:00","Driving website"
"192.168.2.4/boating/is/crazy","-","2009/03/22_00:00","Boating Website"
"192.168.2.5","www.cars.com","2009/04/27_00:00","What a good car website"

所以,您会注意到第一行有 4 列,这是 .csv 格式的理想行。

但是,在第二行中,有 4 列,但第一列仅接受 ip 地址,仅此而已,因此必须删除 192.168.2.2/driving/is/fun 或用 "," .csv 分隔符分隔.

在 vi 中,我可以使用以下内容:

    :/^"\d\{,3}\.\d\{,3}\.\d\{,3}\.\d\{,3}\//s/\//","/

执行以下操作:

  • /^"\d{,3}.\d{,3}.\d{,3}.\d{,3}/ - 设置锚点以在第一个 IP 处开始搜索正斜杠 /。例如,第 2 行:“192.168.2.2/

  • /s///","/ - 替换 IP 地址末尾的 / 并将其替换为 .csv 分隔符 ","

这在 VI/VIM 中效果很好,一次替换了我需要的一行。但是,数据集要大得多,手动使用以下 vi 搜索和替换非常耗时。我正在寻找脚本或找到替代解决方案,因为 VI/VIM 一次只会执行一行,以下 :s/search/replace/g 替换了每行 / 更改日期列。

显然,我尝试了以下方法:

在替换的开头为整个文件添加 %,如下所示:

    :/^"\d\{,3}\.\d\{,3}\.\d\{,3}\.\d\{,3}\//%s/\//","/

突出显示我需要修改但出错的每个条目:

    E492: Not an editor command: /^"\d\{,3}\.\d\{,3}\.\d\{,3}\.\d\{,3}\//%s/\//

这相当令人困惑。

我最终希望使用 sed/perl 编写脚本,一次性编辑整个文件。

所以..

"192.168.2.2/ --> "192.168.2.2","

每行第一次出现。

任何帮助将不胜感激..

谢谢!

【问题讨论】:

  • 只是一个一般性的提示:您可以使用几乎任何字符来分隔您的模式。例如,如果您使用管道,则不必转义斜杠。

标签: regex vim sed awk vi


【解决方案1】:

在 ViM 中,尝试:

 :%s/^\("\d\{,3}\.\d\{,3}\.\d\{,3}\.\d\{,3}\)\(\/[^"]\)/\1","\2

也就是说,我使用全局(%1,$ 的快捷方式,即从第一行到最后一行)替换,而不是搜索/替换。我将您的搜索模式移到了替换模式中,并在不同的组中捕获了 IP 地址和路径。然后将它们替换回来,在两者之间挤压","

【讨论】:

  • 干得好..完美..你能解释一下我错过了什么或你添加了什么,以便我理解/学习吗?
  • 我在答案中添加了它。让我知道是否可以进一步澄清。
  • PEZ,我了解 % = 全局,但分组和“后挤压”的使用对我不起作用。我知道你做了什么,我知道它有效。我只需要了解如何读取/使用语法等。我不完全了解本节的工作原理: (\/[^"])/\1","\2 因为我想应用另一个场景我有IP“192.168.2.1:8080,需要使用“你”必须用“,”替换:
  • 看穿所有这些反斜杠有点困难。 =) ... Parens 形成捕获组。在 ViM 中,默认情况下您必须在括号前面加上反斜杠以赋予它们分组的魔力。因此,您在那里选择的部分是第二个捕获组,它捕获斜线,然后是不是" 的任何内容,然后是分隔符/,右侧有替换模式。 \1 代表第一组捕获的任何东西,\2 因此是第二组。介于两者之间的是文字字符串","。我希望我至少避免进一步混淆它。 =)
  • PEZ,很棒的解释,我很好,我已经做了下一个替换:%s/^("\d\{,3}\.\d\{,3}\. \d\{,3}\.\d\{,3})(\:[^"])/\1","\2 效果很好!节省大量时间!非常感谢您的工作..
【解决方案2】:

在 vi/vim 中,您可以指定要替换的搜索范围。在这种情况下,您希望 :%s 替换所有行:

:%s/search/replace/g

您还可以指定:

:2,5s/search/replace/g      Replace on lines 2-5
:.,$s/search/replace/g      Replace from current line (.) to last line ($)
:.,+3s/search/replace/g     Replace on the current line (.) and the two next lines (+3)
:g/^asd/s/search/replace/g  Replace on lines starting with 'asd'. 

然后,您可以将其与更简单的模式结合起来,在整个文件中进行您想要的替换:

:%s/^\("[^/"]*\)[^"]*"/\1"/

这将从 CSV 中的第一个条目中删除 IP 地址之后的所有内容。

:%s/^\("[^/"]*\)\/\([^"]*\)"/\1","\2/

这会将第一个条目拆分为 IP 地址和其余部分,尽管这只会在 IP 后面有斜杠的那些行中执行。 您要做的是找到模式,转到该行然后替换。在这种情况下添加 '%' 会使命令无效。

【讨论】:

  • John Lawrence,感谢您的回复。“(:%s/search/replace/g)”不起作用。使用 g - 全局选项时,它还会删除日期中的正斜杠,而不仅仅是 IP 末尾的正斜杠,它会删除文件中的每个斜杠。如果您只使用 :%s ,它将删除目录路径和日期等行中的每个正斜杠。
  • 没错。添加“g”会替换该行中的所有匹配项,而没有它只会替换第一个匹配项。这就是为什么我为您应该使用的正则表达式保留了“g”。抱歉,如果不清楚,我的答案的第一部分可供一般参考。
  • 即使在第一次出现正斜杠时使用 :%s/\//","/ 也会删除 IP 后没有正斜杠的订单项上的正斜杠,从而导致删除行中其他位置第一次出现正斜杠,例如日期。
  • 此外,该数据集包含大约 1,000 行。使用集合编号等精确定位行与手动操作一样耗时。范围部分也没有太大帮助。
  • John,this: :%s/^("[^/"]*)[^"]*"/\1"/ 非常接近,除了 ip 所在的行: 192.168.2.1/driving/is/fun 它将 / 替换为 "," 但也删除了driving/is/fun.. 非常接近
【解决方案3】:

你可以用更简单的模式做你想做的事:

s/^\("[^/"]*\)[^"]*"/\1"/

这是:匹配行首,开始一个匹配组:匹配一个",匹配任意数量的不是斜线且不是"的字符,关闭匹配组,匹配任意数量的字符不是",并且匹配"。替换为匹配组内容加上"

上面的模式应该很容易编写脚本。这是一个 Python 示例。

#!/usr/bin/env python
import re
import sys

if len(sys.argv) != 3:
    print("Usage: log_file_cleaner <input_file> <output_file>")
    sys.exit(1)

pat = re.compile(r'^("[^/"]*)[^"]*"')

with open(sys.argv[1]) as in_f, open(sys.argv[2], "w") as out_f:
    for line in in_f:
        line = re.sub(pat, r'\1"', line)
        out_f.write(line)

注意:您需要最新版本的 Python 来执行一次 with 调用,该调用执行两次 open() 调用。如果您卡在 Cygwin 上,可以将上面的内容编辑为两个嵌套的 with 语句,每个语句执行一个 open() 调用。

【讨论】:

  • 史蒂夫哈,感谢您的回复。 s/^("[^/"]*)[^"]*"/\1"/ ^^ 仅突出显示/选择两个“”之间的所有内容。因此它突出显示“192.168.2.1”和“192.168”。 2.2/驾驶/是/乐趣”
  • 您了解什么是匹配组以及它是如何工作的吗?是的,整个模式匹配“/driving/is/fun”的东西,但匹配组省略了它。而且,如果您需要它在第一个引号之后拒绝空格,我可以编辑模式来执行此操作,但您的示例并未表明需要这样做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-17
  • 1970-01-01
  • 2015-05-30
  • 2013-11-15
相关资源
最近更新 更多