4

使用 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","

每行第一次出现。

任何帮助将不胜感激..

谢谢!

4

3 回答 3

4

在 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 后面有斜线的行执行。您要做的是找到模式,转到该行然后替换。在这种情况下添加 '%' 会使命令无效。

于 2012-06-13T20:03:21.403 回答
3

在 ViM 中,尝试:

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

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

于 2012-06-13T20:22:58.363 回答
2

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

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

这是:匹配行首,开始匹配组:匹配 a ",匹配任意数量的非斜杠和非 a 的字符",关闭匹配组,匹配任意数量的非 a 字符",并匹配 a "。替换为匹配组内容加上".

上面的模式应该很容易编写脚本。这是一个 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()调用。

于 2012-06-13T20:03:47.450 回答