从变量字符串中提取IP地址

4

我正在尝试创建一个bash脚本,可以更改phpmyadmin命令文件中的“allow from”IP地址(我还不确定是否可能),并重新启动Apache。

目前,我正在尝试从变量中提取IP地址,经过在网上搜索后,我依然没有头绪,以下是我目前所拥有的...

#bash shell script
#!/bin/bash

clear
echo "Get client IP address"
ip=$(last -i)
echo $ip

exit
echo "restart apache"
/etc/init.d/apache2 reload

我尝试过添加以下行,但没有成功。
ip=$(head -n 1 $ip)

如果有人能告诉我如何从变量$ip中提取第一个IP地址,我将非常感激。


有很多IP地址显示,你如何选择你喜欢的那一个? - konsolebox
如前所述,我想获取第一个实例,即列表中的第一个IP地址。 - mk_89
你使用哪个Unix? - Cyrus
8个回答

10
ip=$(last -i | head -n 1 | awk '{print $3}')

更新:

ip=$(last -i | grep -Pom 1 '[0-9.]{7,15}')

6
您可以使用grepread一起使用:
read ip < <(last -i | grep -o '[0-9]\+[.][0-9]\+[.][0-9]\+[.][0-9]\+')
read ip < <(last -i | grep -Eo '[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+')
  • \b 在这种情况下也可能很有用。但是我不确定它的兼容性。

还有一个:

ip=$(last -i | gawk 'BEGIN { RS = "[ \t\n]"; FS = "." } /^([0-9]+[.]){3}[0-9]+$/ && ! rshift(or(or($1, $2), or($3, $4)), 8) { print ; exit; }')

为什么使用read而不是像这样的简单命令替换:ip=$(last -i base | egrep -o '[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+') - Idriss Neumann
啊!确实,这个解决方案避免了 head -1,加一分 :) - Idriss Neumann
1
@IdrissNeumann ip=$(last -i -1 | egrep -o '[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+') 实际上是最理想的,但 @anubhava 已经提出了这个想法 :) - konsolebox
1
只是一个小问题:egrep已经被弃用了(请参阅man grep),请改用grep -E - terdon
1
@terdon 我明白了,我已经更新了。尽管被弃用,但我怀疑那两个 fgrepegrep 永远不会过时,因为人们不可避免地仍然会继续使用它们。 - konsolebox
很棒的解决方案,我也在我的答案中使用了。 :) +1 - clt60

3
要获取第一个实例,你只需要执行以下操作:
ip=$(last -i -1 | awk '{print $3}')

last -i -1可以输出一个实例的多行内容。例如,日期/时间wtmp开始。 - John B

3

I'd just do

ip=$(last -i -1 | grep -Po '(\d+\.){3}\d+')

上述使用了具有Perl兼容正则表达式的grep,这使我们可以使用\d表示数字。 正则表达式寻找三个[0-9]的重复,后跟一个点(例如123.45.123),然后是另一段数字。 -o标志使grep仅打印匹配行。
这种方法的优点是即使每行字段数发生更改(例如第2个字段为“系统引导”),也能正常工作。 但是,它需要GNU grep,因此如果您需要更便携的解决方案,请改用@konsolebox的答案

0

或者如果你是一个吹毛求疵的人(并且使用-P),你可以测试下面的内容:

while read -r testline
do
    echo "input :=$testline="
    read ip < <(grep -oP '\b(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))\b' <<< "$testline")
    echo "result:=${ip:=NOTFOUND}="
    echo
done <<EOF
some bla bla 127.0.0.1 some
10.10.10.10
bad one 300.200.300.400
some other bla 127.0.0.1 some another 10.1.1.0
10.10.10.10 10.1.1.0
bad one 300.200.300.400 and good 192.168.1.1

above is empty and no ip here too
EOF

它会跳过错误的IP地址,比如800.1.1.1,因此对于上面的测试,它会打印:

input :=some bla bla 127.0.0.1 some=
result:=127.0.0.1=

input :=10.10.10.10=
result:=10.10.10.10=

input :=bad one 300.200.300.400=
result:=NOTFOUND=

input :=some other bla 127.0.0.1 some another 10.1.1.0=
result:=127.0.0.1=

input :=10.10.10.10 10.1.1.0=
result:=10.10.10.10=

input :=bad one 300.200.300.400 and good 192.168.1.1=
result:=192.168.1.1=

input :==
result:=NOTFOUND=

input :=above is empty and no ip here too=
result:=NOTFOUND=

\b 是必需的,以跳过匹配 IP 地址,例如:610.10.10.10,其中包含一个有效的 IP 地址(10.10.10.10)。

该正则表达式取自:https://metacpan.org/pod/Regexp::Common::net


0

由于我碰巧需要在同一领域做一些事情,因此这里提供了一个基本的正则表达式和扩展的正则表达式,松散地匹配IP地址(v4),确保有4个由3个'.'分隔的1-3个数字序列。

# Basic Regular Expression to loosly match an IP address:
bre_match_ip="[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}"

# Extended Regular Expression to loosly match an IP address:
ere_match_ip="[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}"

当从文件(比如HTML)中匹配IP(v4)地址时,很容易无意中匹配到包含版本号的字符串或者URL。下面是我一段时间前为了在Bash脚本中提取有效且唯一(无重复)IP地址而编写的一些Awk代码。它避免了文本中或URL中的版本号,并确保IP地址在范围内。
我知道这对于原帖的作者来说有点过度,也不符合他的需求,但是搜索时可能会遇到这个答案,并发现代码的相对全面性很有用。幸运的是,这段Awk代码有详细的注释,因为它使用了一些稍微晦涩的Awk特性,普通的Awk用户可能不熟悉。
awkExtractIPAddresses='
BEGIN {
    # Regex to match an IP address like sequence (even if too long to be an IP).
    # This is deliberately a loose match, the END section will check for IP
    # address validity.
    ipLikeSequence = "[0-9]+[.][0-9]+[.][0-9]+[.][0-9]+[0-9.]*";

    # Regex to match a number sequence longer than 3 digits.
    digitSequenceTooLongNotIP = "[0-9][0-9][0-9][0-9]+";

    # Regex to match an IP address like sequence which is a version number.
    # Equivalent to "(version|ver|v)[ .:]*" if "tolower($0)" was used.
    versioningNotIP = "[Vv]([Ee][Rr]([Ss][Ii][Oo][Nn])?)?[ .:]*" ipLikeSequence;

    # Regexes to match IP address like sequences next to forward slashes, to
    # avoid version numbers in urls: e.g. http://web.com/libs/1.6.1.0/file.js
    beginsWithFwdSlashNotIP = "[/]" ipLikeSequence;
    endsWithFwdSlashNotIP = ipLikeSequence "[/]";
}
{
    # Set line to the current line (more efficient than using $0 below).
    line = $0;

    # Replace sequences on line which will interfere with extracting genuine
    # IPs. Use a replacement char and not the empty string to avoid accidentally
    # creating a valid IP address from digits on either side of the removed
    # sections. Use "/" as the replacement char for the 2 "FwdSlash" regexes so
    # that multiple number dot slash sequences all get removed, as using "x"
    # could result in inadvertently leaving such a sequence in place.
    # e.g. "/lib1.6.1.0/1.2.3.4/5.6.7.8/file.js" leaves "/lib1.6.1.0xx/file.js"

    gsub(digitSequenceTooLongNotIP, "x", line);
    gsub(versioningNotIP, "x", line);
    gsub(beginsWithFwdSlashNotIP, "/", line);
    gsub(endsWithFwdSlashNotIP, "/", line);

    # Loop through the current line matching IP address like sequences and
    # storing them in the index of the array ipUniqueMatches. By using ipMatch
    # as the array index duplicates are avoided and the values can be easily
    # retrieved by the for loop in the END section. match() automatically sets
    # the built in variables RSTART and RLENGTH.

    while (match(line, ipLikeSequence))
    {
        ipMatch = substr(line, RSTART, RLENGTH);
        ipUniqueMatches[ipMatch];
        line = substr(line, RSTART + RLENGTH + 1);
    }
}
END {
    # Define some IP address related constants.
    ipRangeMin = 0;
    ipRangeMax = 255;
    ipNumSegments = 4;
    ipDelimiter = ".";

    # Loop through the ipUniqueMatches array and print any valid IP addresses.
    # The awk "for each" type of loop is different from the norm. It provides
    # the indexes of the array and NOT the values of the array elements which
    # is more usual in this type of loop.

    for (ipMatch in ipUniqueMatches)
    {
        numSegments = split(ipMatch, ipSegments, ipDelimiter);
        if (numSegments == ipNumSegments &&
            ipSegments[1] >= ipRangeMin && ipSegments[1] <= ipRangeMax &&
            ipSegments[2] >= ipRangeMin && ipSegments[2] <= ipRangeMax &&
            ipSegments[3] >= ipRangeMin && ipSegments[3] <= ipRangeMax &&
            ipSegments[4] >= ipRangeMin && ipSegments[4] <= ipRangeMax)
        {
            print ipMatch;
        }
    }
}'

# Extract valid IP addresses from $fileName, they will each be separated
# by a new line.
awkValidIpAddresses=$(awk "$awkExtractIPAddresses" < "$fileName")

希望这个对你有兴趣。


0

仅使用bash:

read -ra array < <(last -i)
ip="${array[2]}"

或者:

read -ra array < <(last -1 -i)
ip="${array[2]}"

${array[4]}?你是不是想说 ${array[2]}?当字段数量发生变化时,比如在 reboot 行上,这个怎么工作呢? - terdon
@terdon: 是的,是${array[2]},我已经更正了我的回答。 - Idriss Neumann
@terdon:如果字段数量发生变化,我们必须调整命令,对于在此讨论中使用awk的解决方案也是如此(但是如果解析命令不改变,则字段数量不会改变);) - Idriss Neumann
他们确实会这样做,看一下输出。例如尝试使用 last -i | grep reboot - terdon

0
你可以使用Awk。
ip=$(awk '{if(NR == 1) {print $3; exit;}}' < <(last -i))

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