1

自一行结束以来如何替换第二个点?

11.22.mail.su => 11.22@mail.su
22.mails.de => 22@mails.de

等等

我对sedor的示例感兴趣awk

4

5 回答 5

3

至于sed,试试这个:

sed -e 's/\.\([^.]*\.[^.]*\)$/@\1/'

所以:

# echo "11.22.mail.su" | sed -e 's/\.\([^\.]*\.[^\.]*\)$/@\1/g'
11.22@mail.su
# echo "22.mails.de" | sed -e 's/\.\([^\.]*\.[^\.]*\)$/@\1/g'
22@mails.de
于 2012-08-08T20:54:03.377 回答
1

使用awk

awk '{ $0 = gensub( /\.([^.]+\.[^.]+)$/, "@\\1", 1 ); print }' infile

输出:

11.22@mail.su
22@mails.de
于 2012-08-08T21:22:01.877 回答
0

这是一个纯 bash 解决方案(我不建议使用它,如果需要,可以组合各个步骤):

# An extended pattern to match a single field. letters, numbers, and a hyphen
# Add characters if necessary
shopt -s extglob
field='+([[:alnum:]-])'   

for foo in 11.22.mail.su 22.mails.de; do

    # The first part: drop the last two fields and the dots that precede them
    first="${foo%.$field.$field}"

    # The first part, followed by the @, followed by the full string minus the first
    # part and its following dot.
    modified="$first@${foo/#$first.}"

done

更好的办法是使用 bash 的正则表达式支持。

for foo in 11.22.mail.su 22.mails.de; do
    [[ $foo =~ (.*)\.([^.]+\.[^.]+) ]]
    # Three ways to join the two halves with @ 
    one_way="$BASH_REMATCH[1]@${BASH_REMATCH[2]}

    printf -v second_way "%s@%s" ${BASH_REMATCH[@]:1:2}

    SAVE_IFS="$IFS"
    IFS="@"
    third_way="@{BASH_REMATCH[*]:1:2}"
    IFS="$SAVE_IFS"
done
于 2012-08-09T02:35:02.927 回答
0

花了我一秒钟看看你在做什么。提醒一下,这是一个有效的电子邮件地址:

bob@mail.server.com

这是这样的:

bob.smith@mail.server.com

你说从行尾替换第二个句号。这意味着您的正则表达式应该锚定到行尾。$正则表达式末尾的A就是这样做的。

让我们看一下您的示例:

11.22.mail.su

你想匹配.mail.su。让我们从最后一个字符开始,即$. 我们可以通过做来表示任意字符组合.*。这表示从零到行长的任何字符串。句点代表任何字符,而*代表前面的零个或多个。

句点是一个特殊的正则表达式字符,所以我们需要在它前面加上一个反斜杠才能成为句点:\.. 到目前为止,一切都很好。

这应该有效:

\..*\..*$

并且,在我们想要匹配的内容周围加上括号:

(\.)(.*)(\.)(.*)$

那里!第一个 (.) 捕获第二个到最后一个句点。下一个(.*)捕获零个或多个字符,第三个捕获,(.*)捕获该行的其余部分,并将其$锚定在末尾。

除了它实际上不起作用,因为正则表达式是贪婪的。例如,如果我有这个作为我的正则表达式:

.*###

我的字符串如下所示:

first###second###third###fourth

该正则表达式不捕获first###. 它可以捕获最长的字符串,它恰好是first###second###third###.

解决此问题的方法是排除要匹配的字符。在这种情况下,我们不想匹配#. 因此,我们可以这样做:

[^#]*###

那将只匹配first###表示除 之外的任何[^#]字符。表示零个或多个非# 字符。所以,我将把上面表达式中的 替换为除了句点之外的任何字符。#*.*[^.]

前:

(\.)(.*)(\.)(.*)$

后:

(\.)([^.]*)(\.)([^.]*)$

看到第二组和第四组的区别了吗?

还有一个小问题:在sed我正在使用的 中,您必须在括号前加一个反斜杠,否则它们实际上只是字符串()的字符。这是唯一一个你必须在前面加上反斜杠才能让它变得神奇的角色。每个其他神奇的正则表达式字符都是神奇的,除非你在它前面加上一个反斜杠。这意味着而不是这个:

(\.)([^.]*)(\.)([^.]*)$

我们需要这样做:

\(\.\)\([^.]*\)\(\.\)\([^.]*\)$

与上面相同,但现在在每个左括号和右括号之前都有一个反斜杠。

现在,我们有了匹配字符串结尾的内容,让我们进行替换。首先,一个简单的测试:

$ echo "11.22.mail.su" | sed 's/\(\.\)\([^.]*\)\(\.\)\([^.]*\)$/FOO/'
11.12FOO

是的,这与结尾相匹配。接下来,我们可以通过在组号前面加上反斜杠来引用分组:

$ echo "11.22.mail.su" | sed 's/\(\.\)\([^.]*\)\(\.\)\([^.]*\)$/@\2\3\4/'
11.22@mail.su

完美的。请注意,第一组是我的第一个时期。我用@. 接下来,我想保留第二组、第三组和第四组。因此,我的替换字符串是@\2\3\4.

顺便说一句,我真的不需要四个分组。我可以简单地匹配该句点,然后将该行的其余部分作为一个组:

echo "11.22.mail.su" | sed 's/\.\([^.]*\.[^.]*\)$/@\1/'

是的,正则表达式是如此简单易读!我的一个朋友将正则表达式称为水手咒骂,因为在旧漫画中,当有人布置一堆粗俗的东西时,他们会使用可能是正则表达式符号的东西。*

Perl 的一个很好的特性是你可以将一个正则表达式分解成多行,这样你就可以注释正在发生的事情:

#! /usr/bin/env perl

$string = "11.22.mail.su";
$string =~ s/       #Start of my substitution
\.                  #A period
(                   #Start capturing a string
[^.]*               #Everything up to the next period.
\.                  #The next period
[^.]*)$             #And capture it to the end of the line
/@\1/x;             #Replace with a "@" and the rest of the string

print "String = '$string'\n";

$ test.pl
String = '11.22@mail.su'

Perl 的另一个好处是括号有特殊的含义,除非你在它们前面加上反斜杠。(与sed)相反。


有一件事是我顺便提到的,但并没有真正关注。这[^.]*匹配个或多个非周期。这可能是正则表达式的问题。要解决这个问题并强制至少匹配一个,您需要将正则表达式加倍。例如,[^#]*#FOOwill matchTHIS IS A #FOO并且 will match just plain #FOOtoo。

如果我这样做:[^#][^#]*#FOO并将正则表达式加倍,我可以保证在 . 之前至少有一个非#字符#。该正则表达式将匹配THIS IS A #FOO,但不只是简单#FOO的。

所以,我们可能不得不从:

$ sed 's/\(\.\)\([^.]*\)\(\.\)\([^.]*\)$/FOO/'

$ sed 's/\(\.\)\([^.][^.]*\)\(\.\)\([^.][^.]*\)$/FOO/'
于 2012-08-09T03:30:48.970 回答
0

这可能对您有用:

sed 's/\(.*\)\.\(.*\.\)/\1@\2/' file
于 2012-08-08T21:15:33.883 回答