只替换子串的一部分中的字符...会起作用吗?

时间:2012-08-27 20:55:21

标签: bash sed

我提前道歉,以防我忽略了解决问题的方法,但我花了几个小时试图解决这个问题:

我有一个日志文件,这是一个混乱[不是我的错T_T],我需要找到包含某些字符串的行。很好,很简单'直到这一点。一旦我找到它们,我需要替换单词“Before”[或String1]和“is”之间的每个空格[或String2,如果你愿意]使用不同的char [在我的情况下用下划线]。 “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 个答案:

答案 0 :(得分:2)

你可以。 Sed是Turing-complete,所以你可以用它做任何事情。这并不意味着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;
}

答案 1 :(得分: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//删除字符串的原始第一部分。

答案 2 :(得分: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 /'的后半部分获得一个tab char作为替换模式。如果您使用的是vi编辑器,则Ctrl-V Ctrl-I(中间没有空格)将插入选项卡字符。当然,这意味着一个ControlV字符,(按住Ctrl键并按V键),然后按Ctrl I(再次按住Ctrl键,然后按I键)。如果你从基于Windows的编辑器复制粘贴,你可以假设选项卡字符被转换为空格,所以你必须自己解决这个问题。

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

IHTH。

答案 3 :(得分: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 '