如何编写一个 Linux bash 脚本来告诉我 LAN 中哪些计算机处于打开状态?

Posted

技术标签:

【中文标题】如何编写一个 Linux bash 脚本来告诉我 LAN 中哪些计算机处于打开状态?【英文标题】:How can I write a Linux bash script that tells me which computers are ON in my LAN? 【发布时间】:2010-10-18 12:27:20 【问题描述】:

如何编写一个 Linux Bash 脚本来告诉我 LAN 中哪些计算机处于开启状态?

如果我可以给它一个 IP 地址范围作为输入,那会很有帮助。

【问题讨论】:

使用 Nagios nagios.org 而不是每次都运行脚本可能是个好主意,但如果你赶时间,它非常有用 (+1) 【参考方案1】:

我建议使用 nmap 的 ping-scan 标志,

$ nmap -sn 192.168.1.60-70

Starting Nmap 4.11 ( http://www.insecure.org/nmap/ ) at 2009-04-09 20:13 BST
Host machine1.home (192.168.1.64) appears to be up.
Host machine2.home (192.168.1.65) appears to be up.
Nmap finished: 11 IP addresses (2 hosts up) scanned in 0.235 seconds

也就是说,如果你想自己写(这很公平),我会这样做:

for ip in 192.168.1.1..10; do ping -c 1 -t 1 $ip > /dev/null && echo "$ip is up"; done

..以及对上述命令每一位的解释:

生成 IP 地址列表

您可以使用1..10 语法来生成数字列表,例如..

$ echo 1..10
1 2 3 4 5 6 7 8 9 10

(它对于mkdir dir1,dir2/sub1,sub2 之类的东西也很有用——这使得dir1dir2,每个都包含sub1sub2

因此,要生成 IP 列表,我们会执行类似的操作

$ echo 192.168.1.1..10
192.168.1.1 192.168.1.2 [...] 192.168.1.10

循环

要在 bash 中循环某些内容,请使用 for:

$ for thingy in 1 2 3; do echo $thingy; done
1
2
3

ping

接下来,ping .. ping 命令会因操作系统、发行版/版本的不同而有所不同(我目前使用的是 OS X)

默认情况下(同样,在ping 的 OS X 版本上)它会一直 ping 直到被中断,这对这个不起作用,所以ping -c 1 只会尝试发送一个数据包,这应该足以确定机器是否启动。

另一个问题是超时值,在这个版本的 ping 上似乎是 11 秒。它使用-t 标志进行了更改。一秒钟应该足以查看本地网络上的机器是否处于活动状态。

所以,我们将使用的 ping 命令是..

$ ping -c 1 -t 1 192.168.1.1
PING 192.168.1.1 (192.168.1.1): 56 data bytes

--- 192.168.1.1 ping statistics ---
1 packets transmitted, 0 packets received, 100% packet loss

检查 ping 结果

接下来,我们需要知道机器是否回复了..

如果第一个成功,我们可以使用&& 运算符运行命令,例如:

$ echo && echo "It works"

It works
$ nonexistantcommand && echo "This should not echo"
-bash: nonexistantcommand: command not found

很好,我们可以这样做..

ping -c 1 -t 1 192.168.1.1 && echo "192.168.1.1 起来了!"

另一种方法是使用 ping 的退出代码。如果 ping 命令有效,它将以 exit-code 0(成功)退出,如果失败,则以非零代码退出。在 bash 中,您将使用变量 $? 获得最后一个命令退出代码

所以,要检查命令是否有效,我们会这样做..

ping -c 1 -t 1 192.168.1.1;
if [ $? -eq 0 ]; then
    echo "192.168.1.1 is up";
else 
    echo "ip is down";
fi

隐藏 ping 输出

最后,我们不需要看到 ping 输出,所以我们可以通过 > 重定向将 stdout 重定向到 /dev/null,例如:

$ ping -c 1 -t 1 192.168.1.1 > /dev/null && echo "IP is up"
IP is up

要重定向stderr(丢弃ping: sendto: Host is down 消息),请使用2> - 例如:

$ errorcausingcommand
-bash: errorcausingcommand: command not found
$ errorcausingcommand 2> /dev/null
$

脚本

所以,结合所有这些..

for ip in 192.168.1.1..10; do  # for loop and the  operator
    ping -c 1 -t 1 192.168.1.1 > /dev/null 2> /dev/null  # ping and discard output
    if [ $? -eq 0 ]; then  # check the exit code
        echo "$ip is up" # display the output
        # you could send this to a log file by using the >>pinglog.txt redirect
    else
        echo "$ip is down"
    fi
done

或者,使用&& 方法,单行:

for ip in 192.168.1.1..10; do ping -c 1 -t 1 $ip > /dev/null && echo "$ip is up"; done

问题

它很慢.. 每个 ping 命令大约需要 1 秒(因为我们将 -t 超时标志设置为 1 秒)。它一次只能运行一个 ping 命令。解决此问题的明显方法是使用线程,因此您可以运行并发命令,但这超出了您应该使用 bash 的范围..

"Python threads - a first example" 解释了如何使用 Python 线程模块来编写多线程 ping'er.. 虽然那时我还是会再次建议使用nmap -sn..

【讨论】:

有没有办法在 BASH 中创建和启动线程?这可能可以在 ruby​​/python 中作为仅打开网络连接的脚本来完成,但是在 BASH 中可以完成创建多个线程的相同功能吗? 您可以通过执行mycmd & 在后台运行该命令,但这并不是真正的线程,如果您需要多个命令的输出,可能会变得相当复杂.. 它会简单得多只使用 Python/Ruby/etc.. for ip in 192.168.1.1..10; do ping -c 1 -t 1 $ip > /dev/null && echo "$ip is up" & done ; wait 更新:nmap -sP 现已弃用。 nmap -sn 是获得相同输出的当前选项。 为什么不让它并行for ip in 192.168.23.1..254; do (ping -c 1 -t 1 $ip > /dev/null && echo ">>> $ip is up"; ) & done【参考方案2】:

在现实世界中,你可以使用 nmap 来得到你想要的。

nmap -sn 10.1.1.1-255

这将 ping 10.1.1.1 到 10.1.1.255 范围内的所有地址,并让您知道哪些地址会回答。

当然,如果您实际上想将此作为 bash 练习,您可以对每个地址运行 ping 并解析输出,但这是另一回事。

【讨论】:

【参考方案3】:

假设我的网络是 10.10.0.0/24,如果我在广播地址上运行 ping,例如

ping -b 10.10.0.255

我将从该网络上所有未阻止其 ICMP ping 端口的计算机获得答复。

64 bytes from 10.10.0.6: icmp_seq=1 ttl=64 time=0.000 ms
64 bytes from 10.10.0.12: icmp_seq=1 ttl=64 time=0.000 ms 
64 bytes from 10.10.0.71: icmp_seq=1 ttl=255 time=0.000 ms 

所以你只需要提取第 4 列,例如 awk:

ping -b 10.10.0.255 | grep 'bytes from' | awk ' print $4 '

10.10.0.12:
10.10.0.6:
10.10.0.71:
10.10.0.95:

好吧,你会得到重复的,你可能需要删除':'。

从 cmets 编辑: -c 选项限制 ping 的数量 由于脚本将结束,我们还可以限制自己的唯一 IP

ping -c 5 -b 10.10.0.255 | grep 'bytes from' | awk ' print $4 ' | sort | uniq

【讨论】:

ping -b 10.10.0.255 | awk ' 打印 $4 ' |排序 | uniq 将删除重复项 @Ben:你应该在 ping 调用中添加 -c 选项,否则它不会终止。 并非所有 ping 都存在 -b 选项。例如在我的 OpenSuse 上可以,但在我的 Mac 上不行,你仍然可以尝试不使用此选项【参考方案4】:

还有fping:

fping -g 192.168.1.0/24

或:

fping -g 192.168.1.0 192.168.1.255

或仅显示活动的主机:

fping -ag 192.168.1.0/24

它并行 ping 主机,因此扫描速度非常快。我不知道默认安装中包含 fping 的发行版,但在大多数发行版中,您可以通过包管理器获得它。

【讨论】:

【参考方案5】:

同样使用 chburd 指出的“ping 广播地址”方法,这个管道应该可以为您解决问题:

ping -c 5 -b 10.11.255.255 | sed -n 's/.* \([0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+\).*/\1/p' | sort | uniq

当然,您必须将广播地址更改为您网络的地址。

【讨论】:

【参考方案6】:

只是为了好玩,这里有一个备用

#!/bin/bash nmap -sP 192.168.1.0/24 > /dev/null 2>&1 && arp -an | grep -v 不完整 | awk 'print$2' | sed -e s,\(,, | sed -e s,\),,

【讨论】:

可以结合> 变成 >【参考方案7】:

如果您将自己限制为仅更改最后一个八位字节,则此脚本应该可以做到。如何将它从一个八位字节扩展到多个八位字节应该是相当明显的。

#! /bin/bash 基础=$1 开始=$2 结束=$3 计数器=$START 而 [ $counter -le $END ] 做 ip=$BASE.$counter 如果 ping -qc 2 $ip 然后 echo "$ip 响应" 菲 计数器=$(($counter + 1)) 完毕

【讨论】:

【参考方案8】: ip neighbor arp -a Arpwatch

【讨论】:

"ip neighbor" 和 "arp -a" 不会显示您有一段时间(或根本没有)未通信的机器【参考方案9】:

正如其他发帖人指出的那样,nmap 是要走的路,但这里是如何在 bash 中进行相当于 ping 扫描的方法。我不会使用广播 ping,因为现在很多系统都配置为不响应广播 ICMP。

for i in $(seq 1 254); do
    host="192.168.100.$i"
    ping -c 1 -W 1 $host &> /dev/null
    echo -n "Host $host is "
    test $? -eq 0 && echo "up" || echo "down"
done

【讨论】:

【参考方案10】:
#!/bin/bash
#Get the ip address for the range
ip=$(/sbin/ifconfig eth0 | grep 'inet addr:' | cut -d: -f2 | awk ' print $1' | cut -d"." -f1,2,3)

# ping test and list the hosts and echo the info

for range in $ip ; do  [ $? -eq 0 ] && ping -c 1 -w 1 $range > /dev/null 2> /dev/null && echo "Node $range is up" 
done

【讨论】:

【参考方案11】:

虽然是一个老问题,但它似乎仍然很重要(至少对我来说足够重要来处理这个问题)。我的脚本也依赖于 nmap,所以这里没有什么特别的,除了你可以定义你想要扫描的接口并且 IP 范围是自动创建的(至少是那种)。

这是我想出来的

#!/bin/bash
#Script for scanning the (local) network for other computers 

command -v nmap >/dev/null 2>&1 ||  echo "I require nmap but it's not installed. Aborting." >&2; exit 1; 

if [ -n ""$@"" ];  then
    ip=$(/sbin/ifconfig $1 | grep 'inet '  | awk ' print $2' | cut -d"." -f1,2,3 )
    nmap -sP $ip.1-255
else
    echo -e "\nThis is a script for scanning the (local) network for other computers.\n"
    echo "Enter Interface as parameter like this:"
    echo -e "\t./scannetwork.sh $(ifconfig -lu | awk 'print $2')\n"

    echo "Possible interfaces which are up are: "   
    for i in $(ifconfig -lu)
    do
        echo -e "\033[32m \t $i \033[39;49m"
    done

    echo "Interfaces which could be used but are down at the moment: "
    for i in $(ifconfig -ld)
    do
        echo -e "\033[31m \t $i \033[39;49m"
    done
    echo
fi

备注:此脚本是在 OSX 上创建的,因此 linux 环境可能会有一些变化。

【讨论】:

【参考方案12】:

如果您想提供主机列表,可以使用 nmap、grep 和 awk 完成。

安装 nmap:

$ sudo apt-get install nmap

像这样创建文件 hostcheck.sh:

hostcheck.sh

#!/bin/bash

nmap -sP -iL hostlist -oG pingscan > /dev/null
grep Up pingscan | awk 'print $2' > uplist
grep Down pingscan | awk 'print $2' > downlist

-sP:Ping 扫描 - 只需确定主机是否在线

-iL : 来自主机/网络列表的输入

-oG : 以 Grepable 格式输出扫描结果到给定的文件名。

/dev/null : 丢弃输出

更改访问权限:

$ chmod 775 hostcheck.sh

使用要检查的主机列表(主机名或 IP)创建文件主机列表:

主机列表(示例)

192.168.1.1-5
192.168.1.101
192.168.1.123

192.168.1.1-5 是 IP 范围

运行脚本:

./hostcheck.sh 主机文件

将生成包含所有信息的文件 pingscan,与主机联机 (Up) 的 uplist 和与主机脱机 (Down) 的 downlist。

uplist(示例)

192.168.1.1
192.168.1.2
192.168.1.3
192.168.1.4
192.168.1.101

下列表(示例)

192.168.1.5
192.168.1.123

【讨论】:

【参考方案13】:

有些机器不响应 ping(例如防火墙)。

如果你只想要本地网络,你可以使用这个命令:

(for n in $(seq 1 254);do sudo arping -c1 10.0.0.$n &  done ; wait) | grep reply | grep --color -E '([0-9]+\.)3[0-9]+'

解释部分!

    arping 是发送 ARP 请求的命令。它存在于大多数 linux 上。

例子:

sudo arping -c1 10.0.0.14

如果您是 root ofc,则不需要 sudo。

10.0.0.14 : 你要测试的ip -c1 :只发送一个请求。
    &:“我不想等”角色

这是一个非常有用的角色,让您可以在子进程中启动命令而无需等待他完成(如线程)


    for 循环用于解析所有 255 个 IP 地址。它使用seq 命令列出所有数字。

    wait:在我们发起请求后,我们想看看是否有一些回复。为此,我们只需将wait 放在循环之后。 wait 看起来像其他语言中的函数 join()

    ():括号用于将所有输出解释为文本,因此我们可以将其提供给grep

    grep:我们只想看回复。第二个 grep 只是在这里突出显示 IP。

编辑 20150417:Maxi 更新!

我的解决方案的坏处是它会在最后打印所有结果。这是因为 grep 有足够大的缓冲区来放入一些行。 解决方案是将--line-buffered 添加到第一个grep。 像这样:

(for n in $(seq 1 254);do sudo arping -c1 10.0.0.$n &  done ; wait) | grep --line-buffered reply | grep --color -E '([0-9]+\.)3[0-9]+'

【讨论】:

我很欣赏将 arping 作为替代方案的参考,并且防火墙可能不会响应 ping【参考方案14】:
#!/bin/bash

for ((n=0 ; n < 30 ; n+=1))
do
    ip=10.1.1.$n
    if ping -c 1 -w 1 $ip > /dev/null 2> /dev/null >> /etc/logping.txt; then  
        echo "$ip is up" # output up
        # sintax >> /etc/logping.txt log with .txt format
    else
        echo "$ip is down" # output down
    fi
done

【讨论】:

【参考方案15】:

以下(邪恶)代码的运行速度是 nmap 方法的两倍以上

for i in 1..254 ;do (ping 192.168.1.$i -c 1 -w 5  >/dev/null && echo "192.168.1.$i" &) ;done

大约需要 10 秒,而标准的 nmap

nmap -sP 192.168.1.1-254

需要 25 秒...

【讨论】:

【参考方案16】:

嗯,这是我的脚本的一部分。

ship.sh ? 一个简单、方便的网络寻址 ? 具有大量功能的多功能工具 ?

Ping 网络,显示该网络上的在线主机及其本地 IP 和 MAC 地址

它不需要任何编辑。需要root权限才能运行。

GOOGLE_DNS="8.8.8.8"
ONLINE_INTERFACE=$(ip route get "$GOOGLE_DNS" | awk -F 'dev ' 'NR == 1 split($2, a, " "); print a[1]')
NETWORK_IP=$(ip route | awk "/$ONLINE_INTERFACE/ && /src/ print \$1" | cut --fields=1 --delimiter="/")
NETWORK_IP_CIDR=$(ip route | awk "/$ONLINE_INTERFACE/ && /src/ print \$1")
FILTERED_IP=$(echo "$NETWORK_IP" | awk 'BEGINFS=OFS="." NF--')

ip -statistics neighbour flush all &>/dev/null

echo -ne "Pinging $NETWORK_IP_CIDR, please wait ..."
for HOST in 1..254; do
  ping "$FILTERED_IP.$HOST" -c 1 -w 10 &>/dev/null &
done

for JOB in $(jobs -p); do wait "$JOB"; done

ip neighbour | \
    awk 'tolower($0) ~ /reachable|stale|delay|probe/printf ("%5s\t%s\n", $1, $5)' | \
      sort --version-sort --unique

【讨论】:

以上是关于如何编写一个 Linux bash 脚本来告诉我 LAN 中哪些计算机处于打开状态?的主要内容,如果未能解决你的问题,请参考以下文章

linux的shell编程中#!/bin/sh和$bash是啥意思?

从此编写 Bash 脚本不再难

Linux进阶第七天

脚本概述

如果进程死了,如何编写 bash 脚本来重新启动进程?

BASH 学习笔记小结