如何更换自行尾以来的第二个点?
11.22.mail.su => 11.22@mail.su
22.mails.de => 22@mails.de
等
我对sed
或awk
感兴趣的示例。
答案 0 :(得分: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
答案 1 :(得分:1)
使用awk
:
awk '{ $0 = gensub( /\.([^.]+\.[^.]+)$/, "@\\1", 1 ); print }' infile
输出:
11.22@mail.su
22@mails.de
答案 2 :(得分:0)
这可能对您有用:
sed 's/\(.*\)\.\(.*\.\)/\1@\2/' file
答案 3 :(得分: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
答案 4 :(得分:0)
花了一秒钟看看你在做什么。请注意,这是一个有效的电子邮件地址:
bob@mail.server.com
这就是:
bob.smith@mail.server.com
您说从行的结尾替换第二个句点。这意味着您的正则表达式应该锚定到行尾。正则表达式末尾的$
就是这样。
让我们来看看你的例子:
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
相反)。
我有点提到了一件事,但没有真正关注。此[^.]*
匹配 零 或更多非期间。这可能是正则表达式的问题。要解决问题并强制至少匹配一个问题,您需要将正则表达式加倍。例如,[^#]*#FOO
将与THIS IS A #FOO
匹配,并且仅匹配普通#FOO
。
如果我执行此操作:[^#][^#]*#FOO
并将正则表达式加倍,我可以保证在#
之前至少有一个非#
字符。该正则表达式将匹配THIS IS A #FOO
,但不仅仅是普通#FOO
。
所以,我们可能必须从:
$ sed 's/\(\.\)\([^.]*\)\(\.\)\([^.]*\)$/FOO/'
到
$ sed 's/\(\.\)\([^.][^.]*\)\(\.\)\([^.][^.]*\)$/FOO/'