用Python教你写一个批量ping
[TOC]
前言
最近几天,刚好需要配合防火墙替换的割接方案,需要去批量ping测试20+个C
类网段,约5000+个地址,我同事在网上找的工具也不能很好的一次性ping完所有网段的IP
地址,心想,我来帮你搞定,就花些时间劈里啪啦的调试下代码,其中还是遇到一些疑难杂症的,比如下所列:
- 使用的模块,必须得有回显代码
0
或1
让你判断是通还是不通,另外ping
结果也要保存? - 使用的模块,ping回显是通的,但实际用电脑ping是不通的,你说妖不妖?
- 综合测试了
scrapy
、pythonping
和subprocess
模块,最终还是选择subprocess
内置模块符合我心意。
想要写一个批量的ping脚本,首先需要厘清下编写的思路,要怎么去实现?
这是我整理的思路,我会一步一步教大家怎么去实现,咋们接着往下看...
实验环境
- 系统:windows 10 和 Centos7.6
- python工具:Pycharm Pro
- 模块:内置模块
备注:没啥特别的要求。
代码分解
如何读取文本中的所有网段?
说明:通过上下文管理with读取文本中的所有IP网段信息
假如你把收集的所有IP网段都集中放到了一个文本当中,那么我们在执行脚本的时候,怎么把它提取出来呢?
# 存放ip网段的文本路径(当前目录)
IP_INFO_PATH = 'IPnet.cfg'
# 以只读权限打开文本
with open(IP_INFO_PATH, 'r') as f:
# for循环遍历每一行
for line in f.readlines():
#去掉前后空白字符
line = line.strip()
# 忽略空白行或注释行
if ( len(line) == 1 or line.startswith('#') ):
continue
print(line)
执行结果如下所示:
192.168.100.0/24
192.168.101.0/24
192.168.102.0/24
192.168.103.0/24
192.168.104.0/24
代码解释:
- 先定义好存放ip网段的文本路径:
IP_INFO_PATH = 'IPnet.cfg'
- 打开文本通过上下文管理
with
,格式:with open('文本路径','只读方式') as '别名'
-
readlines()
方法可以读取行
信息,并通过for
循环就遍历所有行信息; - len(line) == 1, 表示空白行长度为1,记住不是0。
如何获取网段的主机地址?
说明:该模块我以前有详细写过,请在公众号搜索<用Python帮你实现IP子网计算>
-
如给你一个网段,你怎么去ping呢? 对于我们专业来说,心算就可以得出来了,其他同学就拿起子网计算工具吧,那么,我就通过
ipaddress
模块给大家演示下:假如有一个网段:
192.168.100.0/24
, 快速计算所有的主机地址。# 导入内置模块 import ipaddress IPnet = '192.168.100.0/24' # 创建一个空的列表,用于存放主机地址信息 ip_list = [] # 通过ip_network方法,赋值给temp,这是一个生成器 # 已经计算出所有主机了,需要迭代出来 temp = ipaddress.ip_network(IPnet, strict=False).hosts() # for循环出主机地址,添加到ip_list这个列表 for ip in temp: # print(type(ip), ip) ip_list.append(str(ip)) print(ip_list)
这一步,我们已经掌握如何计算一个网段的所有主机地址了。
如何执行ping程序?
subprocess
是一个内置模块,它可以让你创建一个新的子程序来运行。通常使用如下3个方法:
- subprocess.run()
- subprocess.call()
- subprocess.Popen()
这里,我只介绍简单好用的run()
方法就够满足我们的需求了。
-
一个简单的示例:
import subprocess ip = '114.114.114.114' # windows上cmd的ping操作格式 res = subprocess.run(['ping', '-n', '2', '-w', '500', ip]) print('-'*50) print('类对象:', res) print('什么类型:', type(res)) print('返回代码值:', res.returncode)
执行结果如下所示:
如果不想再终端上看到
ping
的执行结果, 增加参数stdout=subprocess.PIPE
:说明:简单理解就是把执行结果隐藏起来。
# 修改代码如下 res = subprocess.run(['ping', '-n', '2', '-w', '500', ip], stdout=subprocess.PIPE)
-
如何把执行的结果写入到文本当中?
说明:既然隐藏了ping结果,那就要把结果赋值给一个变量,就可以拿到结果了.
import subprocess ip_list = ['8.8.8.8', '114.114.114.114', '114.114.114.115'] for ip in ip_list: res = subprocess.run(['ping', '-n', '2', '-w', '500', ip], stdout=subprocess.PIPE) # 将输出标准流解码成中文 output = res.stdout.decode('gbk') # ping通代码为0 if res.returncode == 0: print('%-20s%-20s' % (ip, 'success')) # ping通结果写入文本 with open('ping_success.txt', 'a+') as f: f.write('%-20s%-20s' % (ip, 'success')) # ping执行结果写入文本 with open('ping_result.txt', 'a+') as f: f.write(output) f.write('-'*50) # ping不通代码为1 else: print('%-20s%-20s' % (ip, 'failure')) # ping不通结果写入文本 with open('ping_failure.txt', 'a+') as f: f.write('%-20s%-20s' % (ip, 'success')) # ping执行结果写入文本 with open('ping_result.txt', 'a+') as f: f.write(output) f.write('-' * 50)
执行结果如下所示:
代码执行完成后自动创建了文件,这里就不把贴出来了,自行打开文本进行验证。
-
如何并发执行ping呢?
使用多进程
multiprocessing
的异步方式来看看并发的效果是怎么样的(多线程threading
就不用了)。说明:
multiprocessing
,简单理解就是通过CPU多核进行并发处理,本示例只演示其功能。from multiprocessing.pool import ThreadPool from datetime import datetime import subprocess THREADING_NUM = 10 pool = ThreadPool(THREADING_NUM) def do_ping(ip): res = subprocess.run(['ping', '-n', '2', '-w', '500', ip], stdout=subprocess.PIPE) output = res.stdout.decode('gbk') if res.returncode == 0: print('%-20s%-20s' % (ip, 'success')) with open('ping_success.txt', 'a+') as f: f.write('%-20s%-20s' % (ip, 'success')) with open('ping_result.txt', 'a+') as f: f.write(output) f.write('-' * 50) else: print('%-20s%-20s' % (ip, 'failure')) with open('ping_failure.txt', 'a+') as f: f.write('%-20s%-20s' % (ip, 'success')) with open('ping_result.txt', 'a+') as f: f.write(output) f.write('-' * 50) if __name__ == '__main__': start_time = datetime.now() ip_list = ['1.1.1.1', '1.1.1.2','1.1.1.3', '8.8.8.8', '114.114.114.114', '114.114.114.115'] for ip in ip_list: pool.apply_async(do_ping, args=(ip,)) pool.close() pool.join() end_time = datetime.now() print('All done.总花费时间{:0.2f}s.'.format((end_time - start_time).total_seconds()))
执行结果如下所示:
说明:具体效果,大家可通过大规模网段进行测试,方可看出其效果明显。
完整代码
-
以下为本章更新的完整代码,大家可在此基础上进行拓展和优化:
#!/usr/bin/env python3 # -*- coding:UTF-8 -*- from multiprocessing import freeze_support from multiprocessing.pool import ThreadPool from datetime import datetime import subprocess import ipaddress import threading import logging import sys # ip网段文本路径(当前目录下) IP_INFO_PATH = 'ip_list.txt' # 线程数() THREADING_NUM = 10 # 进程池 pool = ThreadPool(THREADING_NUM) # 线程锁 queueLock = threading.Lock() # 打印消息 def show_info(msg): queueLock.acquire() print(msg) queueLock.release() # 读取文本,获取ip网段信息 def get_ips_info(): try: with open(IP_INFO_PATH, 'r') as f: for line in f.readlines(): # 去掉前后空白 line = line.strip() # 忽略空格行,len=1 if ( len(line) == 1 or line.startswith('#') ): continue yield line except FileNotFoundError as e: show_info('Can not find "{}"'.format(IP_INFO_PATH)) except IndexError as e: show_info('"{}" format error'.format(IP_INFO_PATH)) def do_one_ping(target_ip): """ 单机ping测试 """ res = '' if sys.platform == 'linux': res = subprocess.run(['ping', '-c', '2', '-W', '1000', target_ip], stdout=subprocess.PIPE) res_out = str(res.stdout.decode('gbk')) if sys.platform == 'win32': res = subprocess.call(['ping', '-n', '2', '-w', '1000', target_ip ], stdout = subprocess.PIPE) else: show_info('不支持该平台系统,非常抱歉!') # print(f'res状态是: {res}') if res.returncode == 0: show_info('%-20s%-20s' % (target_ip, 'success')) else: show_info('%-20s%-20s' % (target_ip, 'failure')) def do_ping(target_ip): """ 批量ping测试 """ try: res , res_out = '', '' # 判断系统平台,执行对应命令 if sys.platform == 'linux': res = subprocess.run(['ping', '-c', '2', '-W', '1000', target_ip], stdout=subprocess.PIPE) res_out = str(res.stdout.decode('gbk')) # 判断系统平台,执行对应命令 if sys.platform == 'win32': res = subprocess.run(['ping', '-n', '2', '-w', '1000', target_ip ], stdout = subprocess.PIPE) res_out = str(res.stdout.decode('gbk')) else: show_info('不支持该平台系统,非常抱歉!') # print(f'res状态是: {res.returncode}') if res.returncode == 0: show_info('%-20s%-20s' % (target_ip, 'success')) # ping成功 with open('LOG' + '/' + 'success_ping_result_' + LogTime + '.txt', 'a+') as f: f.write('%-20s%-20s' % (target_ip, 'success') + '\n') # ping成功结果记录 with open('LOG' + '/' + 'record_ping_' + LogTime + '.txt', 'a+') as f: f.write(res_out) f.write('-' * 50) else: show_info('%-20s%-20s' % (target_ip, 'failure')) # ping失败 with open('LOG' + '/' + 'failure_ping_result_' + LogTime + '.txt', 'a+') as f: f.write('%-20s%-20s' % (target_ip, 'failure') + '\n') # ping失败结果记录 with open('LOG' + '/' + 'record_ping_' + LogTime + '.txt', 'a+') as f: f.write(res_out) f.write('-' * 50) except Exception as e: show_info(e) def get_ip_list(ip): """ 获取ip列表 """ temp = ipaddress.ip_network(ip, False).hosts() ip_list = [] for item in temp: ip_list.append(str(item)) # print(ip_list) return ip_list
if name == 'main': freeze_support() LogTime = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') start_time = datetime.now()
# 单主机ping
# do_one_ping('114.114.114.114')
# 批量执行ping
ips_list = get_ips_info()
for ips in ips_list:
ip_list = get_ip_list(ips)
for ip in ip_list:
pool.apply_async(do_ping, args=(ip,))
pool.close()
pool.join()
end_time = datetime.now()
print('All done.总花费时间{:0.2f}s.'.format((end_time - start_time).total_seconds()))
```
okay, 本章就先介绍这么多了,相信大家有了一个清晰的思路了,再把各个模块ipaddress
、subprocess
、multiprocessing
都熟悉一边,实操干起来,我相信大家能够胜任,并在此基础上更新迭代,写出更好、更优雅的代码,咋们下次见。
码字不易,如觉得本文章有用,请动动手指给个爱心、分享给有需要的朋友拉,多谢各位,晚安。
如果喜欢的我的文章,欢迎关注我的公众号:点滴技术,扫码关注,不定期分享