如何在Linux shell中使用正则表达式从文件中提取IP地址?

70

如何在Linux shell中使用正则表达式提取文本部分?假设我有一个文件,其中每一行都是一个IP地址,但位置不同。最简单的使用常见Unix命令行工具提取这些IP地址的方法是什么?

答案: 在Linux shell中使用grep命令结合正则表达式来提取IP地址,例如:grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' filename。其中,-E选项表示启用扩展正则表达式语法,-o选项表示只输出匹配到的部分。该正则表达式可以匹配任意IP地址格式。

你可以尝试一下我在这里发布的建议:https://unix.stackexchange.com/a/389565/249079 - Ganapathy
grep -E -o '((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?).){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)' file.txt - Anil Agrawal
如果您需要确保不从类似于65465656768128.0.0.1233453的字符串中提取128.0.0.1,请参阅此答案 - Wiktor Stribiżew
22个回答

143
你可以使用 grep 命令来提取它们。
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 - technerdius
grep -o '[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}' file.txt | sort | uniq 用于查找唯一的IP地址。 - Yasiru G
让它更加简洁:grep -o '([0-9]{1,3}.){3}[0-9]{1,3}' - Don
我来晚了,但我希望能够清楚地了解答案。为什么需要在花括号内包含反斜杠?我尝试使用删除这些反斜杠的人给出的答案,但它不起作用。我知道反斜杠允许您的grep将句点读取为文字,但我不明白为什么要将花括号读取为文字。任何澄清都将有所帮助。 - Tom H
你可以通过xargs将所有.txt文件的列表传递给该片段,例如:find | grep ".txt" | xargs grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}' - Andrés Sanchez

47

这里的大多数示例将匹配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 如果你想看到完整匹配的那一行。

这也匹配1233.123.123.123。 - mR_fr0g
2
技术上它与233.123.123.123匹配,即使它之前有一个1。它不限制IP地址之前和之后的内容。 - Sarel Botha
有没有一种方法可以将匹配的IP与路径、文件名和行号一起输出? - Kellen Stuart
如果你添加了 -n 参数,它将打印行号。如果你像 *.txt 这样指定了多个文件,它将打印文件名。如果你使用 -r,它将搜索子目录并打印文件的完整路径。 - Sarel Botha
这个不匹配127.0.0.1,规则要求它应该写成127.000.000.001,这不是标准的写法。另一个例子是8.8.8.8,需要写成008.008.008.008。为什么要给这个点赞36次?我有什么遗漏吗? - anneb

23

这对我来说在访问日志中运行良好。

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}'

'^'是一个锚点,表示在一行的开头进行搜索。


打印无效的IP地址。 - Yokai

11

通常我会从使用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)

4

我写了一个小的脚本来更好地查看我的日志文件,它没有什么特别之处,但可能会帮助很多正在学习Perl的人。在提取IP地址后,它会执行DNS查找。


4
你可以使用sed。但如果你懂perl,那可能更容易,并且从长远来看更有用:
perl -n '/(\d+\.\d+\.\d+\.\d+)/ && print "$1\n"' < file

3
您可以使用我制作的一些Shell助手: https://github.com/philpraxis/ipextract 这里为了方便已经包含了它们:
#!/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

2
 grep -E -o "([0-9]{1,3}[\.]){3}[0-9]{1,3}"

这会打印出无效的IP地址。 - Yokai

2

如果您想要一个现成的解决方案来从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

很好的防止黑客的方法。接下来你可以:
  1. 删除访问量少于20的行
  2. 使用正则表达式截取到单个空格,这样你就只有IP地址了
  3. 使用正则表达式截取IP地址的最后1-3个数字,这样你就只有网络地址了
  4. 在每一行开头添加deny from和一个空格
  5. 将结果文件命名为.htaccess
最初的回答

0

这里的每个人都在使用非常冗长的正则表达式,但实际上理解 POSIX 的正则表达式将使您能够使用像这样的小 grep 命令来打印 IP 地址。

grep -Eo "(([0-9]{1,3})\.){3}([0-9]{1,3})"

(旁注) 这并不忽略无效的IP地址,但它非常简单。

这也匹配 ... - gniourf_gniourf
似乎只有在我将“...”明确地管道传输到命令时才会发生。在第一个括号之前添加“[0-9]”似乎可以解决它。已添加到命令中。如果你发现更多的错误,请告诉我。 - Yokai
我认为你想要 "([0-9]+\.){3}[0-9]+"(否则你仍然会匹配,例如 0...)。 - gniourf_gniourf
我曾经遇到过这个问题。当我在一个使用for循环和tr -dc '0-9.' </dev/urandom | head -c 16创建的文件上使用它时,它会打印出各种错误的行。然而,我认为(([0-9]{1,3})\.){3}([0-9]{1,3})已经接近紧凑形式了,因为它不会打印出不正确的行,比如0...111230..24...。但是,它还没有检查每个八进制数是否为0或大于255。我正在解决这个问题。 - Yokai

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接