1

如果我忽略了我的问题的可用解决方案,我提前道歉,但我花了几个小时试图解决这个问题:

我的日志文件很乱[不是我的错 T_T],我需要找到包含某些字符串的行。到目前为止,一切都很好而且很简单。找到它们后,我需要用不同的字符 [在我的情况下为下划线] 替换单词 "Before" [或 String1] 和 "is" [ 或 String2 之间的每个空格。“String1”之前或“String2”之后的任何内容都不会受到影响。

为了让你知道我应该做什么:

2012-08-27 00:14:55 1346019295409 Before Lorem ipsum dolor sit amet consectetuer Curabitur In id urna ut. Ut massa ac commodo commodo rutrum ac sit neque ante pede. is 47 ms

应该变成:

2012-08-27 00:14:55 1346019295409 Before_Lorem_ipsum_dolor_sit_amet_consectetuer_Curabitur_In_id_urna_ut._Ut_massa_ac_commodo_commodo_rutrum_ac_sit_neque_ante_pede._is 47 ms

由于几乎每个条目的时间戳都不同,我一直在考虑尝试找到一些方法来设置 sed 的限制,但没有运气......

有人可以指出我正确的方向吗?

4

4 回答 4

2

你可以。Sed 是图灵完备的,所以你可以用它做任何事情。这并不意味着 sed 是完成这项工作的好工具:任何不能很好地映射到 sed 命令的东西都会很快变得复杂。如果你坚持使用 sed:

:a
s/\( Before .*\) \(.* is \)/\1_\2/
t a
s/ Before \(.*\) is / Before_\1_is /
s/ Before is / Before_is /

我推荐 awk 。代码更长,但逻辑并没有那么令人头疼。

match($0, / Before (.* )?is /) {
    prefix = substr($0, 1, RSTART + 6);
    middle = substr($0, RSTART + 7, RLENGTH - 10);
    suffix = substr($0, RSTART + RLENGTH - 3);
    gsub(/ /, " ", middle);
    $0 = prefix + middle + suffix;
}
于 2012-08-27T21:55:37.173 回答
2

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

sed 's/ /_/4g;s/_\([^_]*\)_\([^_]*\)$/ \1 \2/' file

解释:

  • s/ /_/4g从第 4 个空格开始,将空格替换为_
  • s/_\([^_]*\)_\([^_]*\)$/ \1 \2/'_用空格替换最后两个。

另一种方法(也许更_安全):

sed 's/\( [^ ]*\)\{2\}$/\n&/;h;s/\n.*//;s/ /_/4g;G;s/\n.*\n//' file

解释:

  • s/\( [^ ]*\)\{2\}$/\n&/在最后两个空格前插入换行符
  • h将模式空间 (PS) 复制到保持空间 (HS)
  • s/\n.*//删除包含最后两个空格的模式。
  • s/ /_/4g在 PS 中用下划线替换除前四个空格之外的所有空格。
  • G将换行符后跟 HS 的内容附加到 PS。
  • s/\n.*\n//删除字符串的原始第一部分。
于 2012-08-28T05:41:32.860 回答
1

可能有一种更优雅的方法可以做到这一点,但是 sed 有很多版本,您可能没有拥有所有酷特性的最新版本。

因此,一个简单的解决方案,假设您对每一行都有相同的格式,将前 3 个空格转换为制表符,一次一个,(这可能对您使用数据的方式有好处),然后转换'_' 字符的所有其他空格。

 sed '
    s/ /      /
    s/ /      /
    s/ /      /
    s/ /_/g' file > newFile

编辑,感谢 David Yaw 在行尾指出所需的 2 个空格,我知道这不是那么容易 :-)。所以..您可以将以下内容添加到上述脚本中,再次依赖于您想要进行已知数量的替换的想法;在这里,我们找到最后 2 个 '_' 字符并将它们替换为空格,

    '....
     s/\([^_][^_]*\)_\([^_][^_]*\)$/\1 \2/
     s/\([^_][^_]*\)_\([^_][^_]*\)$/\1 \2/' file > newFile

较新的 sed 可能不尊重逃脱的父母来捕获一个群体。如果上述方法不起作用,请尝试从每行中删除所有 4 个 '\' 字符。

请注意,当然,您必须做正确的事情才能在 s/srchTarg/replPat/' 的第二半部分中获得一个制表符作为替换模式。如果您使用 vi 编辑器,Ctrl-V Ctrl-I(中间没有空格)将插入一个制表符。当然,这意味着一个 ControlV 字符(按住 Ctrl 键并按 V),然后是 Ctrl I(再次按住 Ctrl 键,然后按 I)。如果您从基于 Windows 的编辑器中复制粘贴,您可以假设制表符正在转换为空格,因此您必须自己解决这个问题。

另请注意,您可以使用其他字符而不是制表符,可能是“:”或“|”,作为最后一步,s/|/ /g将它们转换回空格。

IHTH。

于 2012-08-27T21:12:31.683 回答
1

用 Perl 试试

perl -ne '$_ =~ s/(?<=Before)(.*)(?=is)/$a=$1;$a=~s! !_!g; $a/e; print '

用 -e 调用 Perl 执行用单引号括起来的语句。(?<=)是积极的向后看。它匹配之后的所有内容。(?=)是一个积极的前瞻。它匹配之前的所有内容。(.*)匹配两者之间的整个字符串并捕获 $1 中的匹配项。我s///e修饰符一起使用。这迫使 Perl 将/$a=$1;$a=~s! !_!g; $a其视为 Perl 代码并执行它。

只需尝试:

echo "2012-08-27 00:14:55 1346019295409 Before Lorem ipsum dolor sit amet consectetuer Curabitur In id urna ut. Ut massa ac commodo commodo rutrum ac sit ne que ante petryde. is 47 ms" |
perl -ne '$_ =~ s/(?<=Before)(.*)(?=is)/$a=$1;$a=~s! !_!g; $a/e; print '
于 2012-08-27T23:06:59.333 回答