如何在Linux shell中使用正则表达式提取文本部分?假设我有一个文件,其中每一行都是一个IP地址,但位置不同。最简单的使用常见Unix命令行工具提取这些IP地址的方法是什么?
答案: 在Linux shell中使用grep命令结合正则表达式来提取IP地址,例如:grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' filename。其中,-E选项表示启用扩展正则表达式语法,-o选项表示只输出匹配到的部分。该正则表达式可以匹配任意IP地址格式。如何在Linux shell中使用正则表达式提取文本部分?假设我有一个文件,其中每一行都是一个IP地址,但位置不同。最简单的使用常见Unix命令行工具提取这些IP地址的方法是什么?
答案: 在Linux shell中使用grep命令结合正则表达式来提取IP地址,例如:grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' filename。其中,-E选项表示启用扩展正则表达式语法,-o选项表示只输出匹配到的部分。该正则表达式可以匹配任意IP地址格式。grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' file.txt
egrep -o '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' file
。 - technerdiusfind | grep ".txt" | xargs grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'
。 - Andrés Sanchez这里的大多数示例将匹配999.999.999.999,但这实际上不是一个有效的IP地址。
以下内容将仅匹配有效的IP地址(包括网络和广播地址)。
grep -E -o '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' file.txt
省略 -o 如果你想看到完整匹配的那一行。这对我来说在访问日志中运行良好。
cat access_log | egrep -o '([0-9]{1,3}\.){3}[0-9]{1,3}'
让我们一步一步地来解析。
[0-9]{1,3}
表示[]中提到的范围出现一到三次。在本例中,它是0-9。因此它匹配像10或183这样的模式。
紧随其后的是'.'。我们需要转义它,因为'.'是一个元字符,在shell中有特殊的含义。
所以现在我们得到了像'123.'、'12.'等模式。
这个模式连续重复三次(带有'.')。所以我们将它包含在括号中。
([0-9]{1,3}\.){3}
最后,这个模式又重复了一次,但这次没有'.'。这就是为什么我们在第三步中单独保留它的原因。[0-9]{1,3}
如果IP地址在每行开头(如我的情况),请使用:
egrep -o '^([0-9]{1,3}\.){3}[0-9]{1,3}'
'^'是一个锚点,表示在一行的开头进行搜索。
通常我会从使用grep开始,以便正确获取正则表达式。
# [multiple failed attempts here]
grep '[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*' file # good?
grep -E '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' file # good enough
然后我会尝试将其转换为sed
以过滤出其余行的内容。(在阅读了这个线程之后,你和我都不再这样做了: 我们要改用grep -o
)
sed -ne 's/.*\([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\).*/\1/p # FAIL
那时我通常会因为 sed
不使用与其他人相同的正则表达式而感到恼火。所以我转向了 perl
。
$ perl -nle '/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/ and print $&'
无论如何,了解Perl总是很好的。如果你已经安装了一小部分CPAN,你甚至可以在几乎没有成本的情况下使它更加可靠:
$ perl -MRegexp::Common=net -nE '/$RE{net}{IPV4}/ and say $&' file(s)
#!/bin/sh
ipextract ()
{
egrep --only-matching -E '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)'
}
ipextractnet ()
{
egrep --only-matching -E '(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)/[[:digit:]]+'
}
ipextracttcp ()
{
egrep --only-matching -E '[[:digit:]]+/tcp'
}
ipextractudp ()
{
egrep --only-matching -E '[[:digit:]]+/udp'
}
ipextractsctp ()
{
egrep --only-matching -E '[[:digit:]]+/sctp'
}
ipextractfqdn ()
{
egrep --only-matching -E '[a-zA-Z0-9]+[a-zA-Z0-9\-\.]*\.[a-zA-Z]{2,}'
}
从shell中加载或源化它(当存储在ipextract文件中):
$ . ipextract
使用它们:
$ ipextract < /etc/hosts
127.0.0.1
255.255.255.255
$
以下是一些实际使用案例:
ipextractfqdn < /var/log/snort/alert | sort -u
dmesg | ipextractudp
grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}"
如果您想要一个现成的解决方案来从Apache日志中获取IP地址并列出每个IP地址访问网站的次数,请使用以下代码:
Original Answer翻译成"最初的回答"
grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' error.log | sort | uniq -c | sort -nr > occurences.txt
deny from
和一个空格这里的每个人都在使用非常冗长的正则表达式,但实际上理解 POSIX 的正则表达式将使您能够使用像这样的小 grep
命令来打印 IP 地址。
grep -Eo "(([0-9]{1,3})\.){3}([0-9]{1,3})"
...
。 - gniourf_gniourf"([0-9]+\.){3}[0-9]+"
(否则你仍然会匹配,例如 0...
)。 - gniourf_gniourftr -dc '0-9.' </dev/urandom | head -c 16
创建的文件上使用它时,它会打印出各种错误的行。然而,我认为(([0-9]{1,3})\.){3}([0-9]{1,3})
已经接近紧凑形式了,因为它不会打印出不正确的行,比如0...111230..24...
。但是,它还没有检查每个八进制数是否为0
或大于255
。我正在解决这个问题。 - Yokai
65465656768128.0.0.1233453
的字符串中提取128.0.0.1
,请参阅此答案。 - Wiktor Stribiżew