提交修改
This commit is contained in:
parent
d2c505fd07
commit
04eca9d126
|
|
@ -0,0 +1,192 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import subprocess
|
||||
import platform
|
||||
import ipaddress
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from tabulate import tabulate
|
||||
import colorama
|
||||
from colorama import Fore, Style
|
||||
|
||||
# 初始化颜色设置
|
||||
colorama.init(autoreset=True)
|
||||
|
||||
|
||||
def ping_ip(ip):
|
||||
"""Ping单个IP地址,返回是否可达"""
|
||||
param = '-n' if platform.system().lower() == 'windows' else '-c'
|
||||
timeout = '-w' if platform.system().lower() == 'windows' else '-W'
|
||||
command = ['ping', param, '1', timeout, '500', str(ip)]
|
||||
|
||||
try:
|
||||
response = subprocess.run(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, timeout=1)
|
||||
return ip, response.returncode == 0
|
||||
except subprocess.TimeoutExpired:
|
||||
return ip, False
|
||||
|
||||
|
||||
def parse_ip_range(ip_range):
|
||||
"""解析IP范围字符串(如10.10.0.1 或 10.10.0.1-10.10.0.255)"""
|
||||
# 如果只输入了一个IP
|
||||
if '-' not in ip_range:
|
||||
# 提取网络前缀
|
||||
ip_parts = ip_range.split('.')
|
||||
if len(ip_parts) != 4:
|
||||
raise ValueError("请输入有效的IPv4地址")
|
||||
|
||||
# 构建C类网段范围
|
||||
start_ip = ip_range
|
||||
end_ip = f"{ip_parts[0]}.{ip_parts[1]}.{ip_parts[2]}.255"
|
||||
print(f"自动扩展扫描范围: {start_ip} 到 {end_ip}")
|
||||
else:
|
||||
start_ip, end_ip = ip_range.split('-')
|
||||
|
||||
start = ipaddress.ip_address(start_ip)
|
||||
end = ipaddress.ip_address(end_ip)
|
||||
|
||||
# 提取网络前缀
|
||||
prefix = '.'.join(str(start).split('.')[:-1]) + '.'
|
||||
|
||||
ip_list = []
|
||||
current = start
|
||||
while current <= end:
|
||||
ip_list.append(current)
|
||||
current += 1
|
||||
|
||||
return ip_list, prefix
|
||||
|
||||
|
||||
def ip_to_int(ip):
|
||||
"""将IP地址转换为整数用于排序"""
|
||||
return int(ipaddress.ip_address(ip))
|
||||
|
||||
|
||||
def scan_network(ip_range):
|
||||
"""扫描IP范围并返回结果"""
|
||||
try:
|
||||
ip_list, prefix = parse_ip_range(ip_range)
|
||||
except Exception as e:
|
||||
print(f"{Fore.RED}错误: {e}{Style.RESET_ALL}")
|
||||
return None, None
|
||||
|
||||
results = []
|
||||
|
||||
print(f"扫描中: {len(ip_list)} 个IP地址...")
|
||||
|
||||
with ThreadPoolExecutor(max_workers=100) as executor:
|
||||
futures = [executor.submit(ping_ip, ip) for ip in ip_list]
|
||||
|
||||
for i, future in enumerate(futures):
|
||||
ip, status = future.result()
|
||||
results.append((str(ip), status))
|
||||
|
||||
# 显示进度
|
||||
if (i + 1) % 10 == 0 or (i + 1) == len(ip_list):
|
||||
online_count = sum(1 for _, status in results if status)
|
||||
offline_count = sum(1 for _, status in results if not status)
|
||||
# 使用多行字符串避免语法错误
|
||||
progress_msg = (
|
||||
f"\r进度: {i + 1}/{len(ip_list)} | "
|
||||
f"{Fore.GREEN}在线: {online_count}{Style.RESET_ALL} | "
|
||||
f"{Fore.RED}离线: {offline_count}{Style.RESET_ALL}"
|
||||
)
|
||||
print(progress_msg, end='')
|
||||
|
||||
print("\n扫描完成!")
|
||||
return results, prefix
|
||||
|
||||
|
||||
def display_results(results, prefix):
|
||||
"""以紧凑格式显示结果,每行20个IP"""
|
||||
# 按IP地址排序
|
||||
sorted_results = sorted(results, key=lambda x: ip_to_int(x[0]))
|
||||
|
||||
# 提取IP最后一段和状态
|
||||
compact_results = []
|
||||
for ip, status in sorted_results:
|
||||
host_part = ip.split('.')[-1]
|
||||
compact_results.append((host_part, status))
|
||||
|
||||
# 计算统计信息
|
||||
online_count = sum(1 for _, status in results if status)
|
||||
offline_count = sum(1 for _, status in results if not status)
|
||||
total_count = len(results)
|
||||
|
||||
# 显示结果
|
||||
print("\n" + "=" * 80)
|
||||
summary = (
|
||||
f"扫描结果摘要: {prefix}0/24 | 总计 {total_count} 个IP | "
|
||||
f"{Fore.GREEN}在线: {online_count}{Style.RESET_ALL} | "
|
||||
f"{Fore.RED}离线: {offline_count}{Style.RESET_ALL}"
|
||||
)
|
||||
print(summary)
|
||||
print("=" * 80 + "\n")
|
||||
|
||||
# 准备表格数据 - 每行20个IP
|
||||
table_data = []
|
||||
|
||||
# 将数据分组,每组20个IP
|
||||
for i in range(0, len(compact_results), 20):
|
||||
row = compact_results[i:i + 20]
|
||||
table_row = []
|
||||
for host, status in row:
|
||||
# 使用ASCII字符替代Unicode图标
|
||||
if status:
|
||||
display_text = f"{Fore.GREEN}{host}*{Style.RESET_ALL}" # 使用星号*表示在线
|
||||
else:
|
||||
display_text = f"{Fore.RED}{host}x{Style.RESET_ALL}" # 使用x表示离线
|
||||
table_row.append(display_text)
|
||||
|
||||
# 填充不足20个的行
|
||||
while len(table_row) < 20:
|
||||
table_row.append("")
|
||||
|
||||
table_data.append(table_row)
|
||||
|
||||
# 输出表格(不显示列号标题)
|
||||
print(tabulate(table_data, tablefmt="simple_grid", stralign="center"))
|
||||
|
||||
# 添加图例说明
|
||||
print(f"\n图例: {Fore.GREEN}* 在线{Style.RESET_ALL} | {Fore.RED}x 离线{Style.RESET_ALL}")
|
||||
print(f"注: 数字表示IP地址最后一段,例如 '1*' 表示 {prefix}1 在线")
|
||||
|
||||
|
||||
def show_menu():
|
||||
"""显示菜单选项"""
|
||||
print("\n" + "=" * 60)
|
||||
print(f"{Fore.CYAN}IP网段扫描工具{Style.RESET_ALL}")
|
||||
print("=" * 60)
|
||||
print("功能说明:")
|
||||
print(f" - 输入单个IP (如 {Fore.YELLOW}10.10.0.1{Style.RESET_ALL}) 将自动扫描整个网段")
|
||||
print(f" - 输入IP范围 (如 {Fore.YELLOW}10.10.0.1-10.10.0.100{Style.RESET_ALL}) 扫描指定范围")
|
||||
print(f" - 输入 {Fore.RED}0{Style.RESET_ALL} 退出程序")
|
||||
print("=" * 60)
|
||||
|
||||
|
||||
def main():
|
||||
while True:
|
||||
show_menu()
|
||||
|
||||
ip_range = input("\n请输入要扫描的IP地址或范围: ").strip()
|
||||
|
||||
# 退出条件
|
||||
if ip_range == '0':
|
||||
print(f"{Fore.YELLOW}感谢使用,程序已退出!{Style.RESET_ALL}")
|
||||
break
|
||||
|
||||
# 验证输入是否为空
|
||||
if not ip_range:
|
||||
print(f"{Fore.RED}错误: 输入不能为空{Style.RESET_ALL}")
|
||||
continue
|
||||
|
||||
# 执行扫描
|
||||
results, prefix = scan_network(ip_range)
|
||||
|
||||
# 显示结果(如果扫描成功)
|
||||
if results and prefix:
|
||||
display_results(results, prefix)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -0,0 +1,285 @@
|
|||
import subprocess
|
||||
import platform
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import yaml
|
||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||
from colorama import Fore, Style, init
|
||||
|
||||
# 初始化colorama
|
||||
init(autoreset=True)
|
||||
|
||||
|
||||
def load_config(file_path='config.yml', base_dir=None):
|
||||
"""从YAML配置文件加载IP列表和注释"""
|
||||
if base_dir:
|
||||
file_path = os.path.join(base_dir, file_path)
|
||||
|
||||
# 检查文件是否存在
|
||||
if not os.path.exists(file_path):
|
||||
raise FileNotFoundError(f"配置文件不存在: {file_path}")
|
||||
|
||||
try:
|
||||
with open(file_path, 'r', encoding='utf-8') as f:
|
||||
config = yaml.safe_load(f)
|
||||
except yaml.YAMLError as e:
|
||||
raise ValueError(f"YAML格式错误: {e}")
|
||||
|
||||
# 获取IP列表和注释
|
||||
ip_comments = []
|
||||
if 'IP_LIST' in config and isinstance(config['IP_LIST'], dict):
|
||||
for ip, comment in config['IP_LIST'].items():
|
||||
ip = str(ip).strip()
|
||||
comment = str(comment).strip() if comment else ""
|
||||
|
||||
# 验证是否为有效的IP地址或域名
|
||||
if ip and not ip.startswith('#'):
|
||||
ip_comments.append((ip, comment))
|
||||
|
||||
if not ip_comments:
|
||||
raise ValueError("未找到有效的IP地址配置,请检查config.yml中的IP_LIST部分")
|
||||
|
||||
# 返回IP列表、注释字典和可选参数
|
||||
ip_list = [item[0] for item in ip_comments]
|
||||
comment_dict = {item[0]: item[1] for item in ip_comments}
|
||||
|
||||
# 获取可选参数
|
||||
settings = config.get('SETTINGS', {})
|
||||
if not isinstance(settings, dict):
|
||||
settings = {}
|
||||
|
||||
optional_params = {
|
||||
'count': max(1, min(10, settings.get('count', 2))), # 限制范围1-10
|
||||
'timeout': max(1, min(10, settings.get('timeout', 2))), # 限制范围1-10秒
|
||||
'max_workers': max(1, min(50, settings.get('max_workers', 10))) # 限制范围1-50
|
||||
}
|
||||
|
||||
return ip_list, comment_dict, optional_params
|
||||
|
||||
|
||||
def ping_ip(ip, count=2, timeout=2):
|
||||
"""执行ping操作并返回结果"""
|
||||
# 跨平台参数处理
|
||||
param = '-n' if platform.system().lower() == 'windows' else '-c'
|
||||
timeout_param = '-w' if platform.system().lower() == 'windows' else '-W'
|
||||
|
||||
# 构建命令
|
||||
command = ['ping', param, str(count)]
|
||||
if platform.system().lower() != 'windows':
|
||||
command.extend([timeout_param, str(timeout * 1000)]) # Linux/macOS使用毫秒
|
||||
else:
|
||||
command.extend(['-w', str(timeout * 1000)]) # Windows使用毫秒
|
||||
|
||||
command.append(ip)
|
||||
|
||||
# 使用subprocess.DEVNULL兼容所有平台
|
||||
try:
|
||||
response = subprocess.run(
|
||||
command,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
timeout=timeout + count,
|
||||
check=True
|
||||
)
|
||||
return ip, True
|
||||
except (subprocess.CalledProcessError, subprocess.TimeoutExpired):
|
||||
return ip, False
|
||||
|
||||
|
||||
def check_ips(ip_list, comment_dict, count=2, timeout=2, max_workers=10):
|
||||
"""并发检查多个IP的连通性,带实时进度显示"""
|
||||
results = {}
|
||||
total = len(ip_list)
|
||||
completed = 0
|
||||
online_count = 0
|
||||
offline_count = 0
|
||||
|
||||
print(f"\n{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}开始检测 {total} 个IP地址的连通性...{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}\n")
|
||||
|
||||
with ThreadPoolExecutor(max_workers=max_workers) as executor:
|
||||
futures = {executor.submit(ping_ip, ip, count, timeout): ip for ip in ip_list}
|
||||
|
||||
for future in as_completed(futures):
|
||||
try:
|
||||
ip, status = future.result()
|
||||
results[ip] = status
|
||||
completed += 1
|
||||
|
||||
if status:
|
||||
online_count += 1
|
||||
status_icon = f"{Fore.GREEN}✓{Style.RESET_ALL}"
|
||||
else:
|
||||
offline_count += 1
|
||||
status_icon = f"{Fore.RED}✗{Style.RESET_ALL}"
|
||||
|
||||
# 获取注释
|
||||
comment = comment_dict.get(ip, "")
|
||||
comment_str = f" - {comment}" if comment else ""
|
||||
|
||||
# 实时显示检测结果
|
||||
print(f" [{completed}/{total}] {status_icon} {ip:<20}{comment_str}")
|
||||
|
||||
except Exception as e:
|
||||
completed += 1
|
||||
offline_count += 1
|
||||
print(f" [{completed}/{total}] {Fore.RED}✗{Style.RESET_ALL} {ip:<20} - {Fore.YELLOW}错误: {str(e)}{Style.RESET_ALL}")
|
||||
|
||||
# 打印总结
|
||||
print(f"\n{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}检测完成!{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}\n")
|
||||
|
||||
return results
|
||||
|
||||
|
||||
def save_results(results, comment_dict, file_path='ping_results.json', base_dir=None):
|
||||
"""保存结果到JSON文件"""
|
||||
if base_dir:
|
||||
file_path = os.path.join(base_dir, file_path)
|
||||
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
json.dump({
|
||||
'timestamp': time.strftime("%Y-%m-%d %H:%M:%S"),
|
||||
'results': [
|
||||
{
|
||||
'ip': ip,
|
||||
'status': "Pass" if status else "Fail",
|
||||
'comment': comment_dict.get(ip, "")
|
||||
}
|
||||
for ip, status in results.items()
|
||||
]
|
||||
}, f, indent=2, ensure_ascii=False)
|
||||
|
||||
|
||||
def create_sample_config(file_path='config.yml', base_dir=None):
|
||||
"""创建示例YAML配置文件"""
|
||||
if base_dir:
|
||||
file_path = os.path.join(base_dir, file_path)
|
||||
|
||||
if not os.path.exists(file_path):
|
||||
# 使用注释格式创建更友好的YAML文件
|
||||
with open(file_path, 'w', encoding='utf-8') as f:
|
||||
f.write("""# Ping多多配置文件 - YAML格式
|
||||
# 每行一个IP地址,冒号后添加注释(可选)
|
||||
|
||||
IP_LIST:
|
||||
# DNS服务器
|
||||
8.8.8.8: "Google DNS"
|
||||
192.168.1.1: "路由器"
|
||||
|
||||
# 其他示例
|
||||
example.com: "示例网站"
|
||||
10.0.0.1: ""
|
||||
|
||||
# 可选参数配置
|
||||
SETTINGS:
|
||||
count: 2 # ping次数
|
||||
timeout: 2 # 超时时间(秒)
|
||||
max_workers: 10 # 并发线程数
|
||||
""")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 显示欢迎信息
|
||||
print(f"\n{Fore.CYAN}{'='*60}{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}🔍 Ping多多 - IP连通性检测工具 v2.0{Style.RESET_ALL}")
|
||||
print(f"{Fore.CYAN}{'='*60}{Style.RESET_ALL}\n")
|
||||
|
||||
# 获取当前脚本所在目录
|
||||
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||
|
||||
# 创建示例配置文件(如果不存在)
|
||||
create_sample_config(base_dir=script_dir)
|
||||
|
||||
try:
|
||||
# 加载配置(优先使用YAML格式)
|
||||
config_file = os.path.join(script_dir, 'config.yml')
|
||||
if not os.path.exists(config_file):
|
||||
# 如果YAML不存在,尝试旧的INI格式
|
||||
config_file = os.path.join(script_dir, 'config.ini')
|
||||
|
||||
ip_list, comment_dict, optional_params = load_config(file_path=config_file)
|
||||
count = optional_params['count']
|
||||
timeout = optional_params['timeout']
|
||||
max_workers = optional_params['max_workers']
|
||||
|
||||
# 执行ping测试
|
||||
print(f"正在测试 {len(ip_list)} 个IP地址的连通性...")
|
||||
print(f"参数: count={count}, timeout={timeout}s, max_workers={max_workers}")
|
||||
start_time = time.time()
|
||||
results = check_ips(ip_list, comment_dict, count, timeout, max_workers)
|
||||
elapsed = time.time() - start_time
|
||||
|
||||
# 输出汇总结果(带注释)
|
||||
print(f"\n{Fore.YELLOW}{'='*60}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}📊 检测结果汇总{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}{'='*60}{Style.RESET_ALL}\n")
|
||||
|
||||
# 先输出开机的
|
||||
print(f"{Fore.GREEN}【开机】:{Style.RESET_ALL}")
|
||||
online_found = False
|
||||
for ip, status in results.items():
|
||||
if status:
|
||||
online_found = True
|
||||
comment = comment_dict.get(ip, "")
|
||||
display_text = f"{ip} ({comment})" if comment else ip
|
||||
print(f" {Fore.GREEN}✓{Style.RESET_ALL} {display_text}")
|
||||
|
||||
if not online_found:
|
||||
print(f" {Fore.LIGHTBLACK_EX}无{Style.RESET_ALL}")
|
||||
|
||||
# 再输出关机的
|
||||
print(f"\n{Fore.RED}【关机】:{Style.RESET_ALL}")
|
||||
offline_found = False
|
||||
for ip, status in results.items():
|
||||
if not status:
|
||||
offline_found = True
|
||||
comment = comment_dict.get(ip, "")
|
||||
display_text = f"{ip} ({comment})" if comment else ip
|
||||
print(f" {Fore.RED}✗{Style.RESET_ALL} {display_text}")
|
||||
|
||||
if not offline_found:
|
||||
print(f" {Fore.LIGHTBLACK_EX}无{Style.RESET_ALL}")
|
||||
|
||||
print(f"\n{Fore.YELLOW}{'='*60}{Style.RESET_ALL}")
|
||||
success_count = sum(1 for s in results.values() if s)
|
||||
fail_count = sum(1 for s in results.values() if not s)
|
||||
total_count = len(ip_list)
|
||||
|
||||
# 计算成功率
|
||||
success_rate = (success_count / total_count * 100) if total_count > 0 else 0
|
||||
|
||||
print(f"{Fore.CYAN}📈 统计信息:{Style.RESET_ALL}")
|
||||
print(f" {Fore.WHITE}服务器总数 : {total_count}{Style.RESET_ALL}")
|
||||
print(f" {Fore.GREEN}✓ 开机: {success_count}{Style.RESET_ALL} {Fore.RED}✗ 关机: {fail_count}{Style.RESET_ALL}")
|
||||
print(f" {Fore.BLUE}⏱ 耗时: {elapsed:.2f}秒{Style.RESET_ALL}")
|
||||
print(f" {Fore.YELLOW}📊 成功率: {success_rate:.1f}%{Style.RESET_ALL}")
|
||||
|
||||
# # 根据成功率显示不同的提示
|
||||
# if success_rate >= 90:
|
||||
# print(f"\n{Fore.GREEN}✅ 系统状态良好!{Style.RESET_ALL}")
|
||||
# elif success_rate >= 70:
|
||||
# print(f"\n{Fore.YELLOW}⚠️ 部分服务器离线,请检查{Style.RESET_ALL}")
|
||||
# else:
|
||||
# print(f"\n{Fore.RED}❌ 大量服务器离线,需要立即处理!{Style.RESET_ALL}")
|
||||
|
||||
print(f"\n{Fore.YELLOW}{'='*60}{Style.RESET_ALL}\n")
|
||||
|
||||
# 保存结果
|
||||
save_results(results, comment_dict, base_dir=script_dir)
|
||||
print(f"{Fore.GREEN}✓ 结果已保存到 ping_results.json{Style.RESET_ALL}\n")
|
||||
|
||||
except FileNotFoundError as e:
|
||||
print(f"\n{Fore.RED}✗ 错误: {e}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}提示: 首次运行会自动创建示例配置文件{Style.RESET_ALL}\n")
|
||||
except ValueError as e:
|
||||
print(f"\n{Fore.RED}✗ 配置错误: {e}{Style.RESET_ALL}")
|
||||
print(f"{Fore.YELLOW}请检查config.yml文件格式是否正确{Style.RESET_ALL}\n")
|
||||
except Exception as e:
|
||||
print(f"\n{Fore.RED}✗ 未知错误: {e}{Style.RESET_ALL}")
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
print()
|
||||
|
|
@ -0,0 +1,176 @@
|
|||
# Ping多多 - IP连通性检测工具
|
||||
|
||||
## 📋 功能说明
|
||||
|
||||
批量检测多个IP地址的连通性,支持并发ping测试,**实时显示检测进度**,结果分类显示并保存为JSON文件。
|
||||
|
||||
## ✨ v2.0 新特性
|
||||
|
||||
- 🎨 **彩色输出**:使用颜色区分在线/离线状态
|
||||
- 📊 **实时进度**:每检测一个IP立即显示结果
|
||||
- 📈 **成功率统计**:自动计算并显示服务器在线率
|
||||
- 💡 **智能提示**:根据成功率给出不同的处理建议
|
||||
- 🔍 **更好的错误提示**:详细的配置错误信息
|
||||
|
||||
## 🚀 快速开始
|
||||
|
||||
### 1. 安装依赖
|
||||
|
||||
```bash
|
||||
pip install pyyaml colorama
|
||||
```
|
||||
|
||||
### 2. 配置文件格式(YAML)
|
||||
|
||||
编辑 `config.yml` 文件:
|
||||
|
||||
```yaml
|
||||
# Ping多多配置文件 - YAML格式
|
||||
# 每行一个IP地址,冒号后添加注释(可选)
|
||||
|
||||
IP_LIST:
|
||||
# Oracle Rac集群
|
||||
10.10.14.220: "Oracle Rac节点1"
|
||||
10.10.14.222: "Oracle Rac存储节点(IP冲突,待调整)"
|
||||
|
||||
# 虚拟化平台
|
||||
10.10.30.161: "天融信3.5.6(DELL R730)(88G+1.7T)"
|
||||
10.10.30.215: "H3C CAS7.0(X10主板)(128G+3.5T)"
|
||||
|
||||
# 可选参数配置
|
||||
SETTINGS:
|
||||
count: 2 # ping次数
|
||||
timeout: 2 # 超时时间(秒)
|
||||
max_workers: 3 # 并发线程数
|
||||
```
|
||||
|
||||
### 3. 运行脚本
|
||||
|
||||
```bash
|
||||
python MyPing配置IP.py
|
||||
```
|
||||
|
||||
## 📝 配置说明
|
||||
|
||||
### IP_LIST 部分
|
||||
- **格式**:`IP地址: "注释"`
|
||||
- **注释可选**:可以留空字符串 `""`
|
||||
- **支持分组**:使用 `#` 注释进行逻辑分组
|
||||
- **每行一个IP**:简洁明了,易于维护
|
||||
|
||||
### SETTINGS 部分(可选)
|
||||
| 参数 | 默认值 | 范围 | 说明 |
|
||||
|------|--------|------|------|
|
||||
| count | 2 | 1-10 | 每个IP的ping次数 |
|
||||
| timeout | 2 | 1-10秒 | 超时时间 |
|
||||
| max_workers | 10 | 1-50 | 并发线程数 |
|
||||
|
||||
## 📊 输出示例
|
||||
|
||||
```
|
||||
============================================================
|
||||
🔍 Ping多多 - IP连通性检测工具 v2.0
|
||||
============================================================
|
||||
|
||||
============================================================
|
||||
开始检测 33 个IP地址的连通性...
|
||||
============================================================
|
||||
|
||||
[1/33] ✓ 10.10.14.221 - Oracle Rac节点2
|
||||
[2/33] ✓ 10.10.14.222 - Oracle Rac存储节点
|
||||
[3/33] ✗ 10.10.14.220 - Oracle Rac节点1
|
||||
...
|
||||
|
||||
============================================================
|
||||
检测完成!
|
||||
============================================================
|
||||
|
||||
============================================================
|
||||
📊 检测结果汇总
|
||||
============================================================
|
||||
|
||||
【开机】:
|
||||
✓ 10.10.14.221 (Oracle Rac节点2)
|
||||
✓ 10.10.30.161 (天融信3.5.6)
|
||||
...
|
||||
|
||||
【关机】:
|
||||
✗ 10.10.14.220 (Oracle Rac节点1)
|
||||
...
|
||||
|
||||
============================================================
|
||||
📈 统计信息:
|
||||
服务器总数 : 33
|
||||
✓ 开机: 14 ✗ 关机: 19
|
||||
⏱ 耗时: 31.47秒
|
||||
📊 成功率: 42.4%
|
||||
|
||||
❌ 大量服务器离线,需要立即处理!
|
||||
|
||||
============================================================
|
||||
|
||||
✓ 结果已保存到 ping_results.json
|
||||
```
|
||||
|
||||
## 🎯 性能优化建议
|
||||
|
||||
### 1. 调整并发数
|
||||
- **少量IP(<50)**:max_workers = 10
|
||||
- **中等规模(50-200)**:max_workers = 20
|
||||
- **大规模(>200)**:max_workers = 30-50
|
||||
|
||||
### 2. 调整超时时间
|
||||
- **局域网**:timeout = 1-2秒
|
||||
- **跨网段**:timeout = 2-3秒
|
||||
- **广域网**:timeout = 3-5秒
|
||||
|
||||
### 3. 调整ping次数
|
||||
- **快速检测**:count = 1
|
||||
- **常规检测**:count = 2-3
|
||||
- **精确检测**:count = 4-5
|
||||
|
||||
## 🔄 版本历史
|
||||
|
||||
### v2.0 (当前版本) - 2024
|
||||
- ✅ 改用 YAML 配置文件格式
|
||||
- ✅ 添加实时进度显示
|
||||
- ✅ 彩色输出界面
|
||||
- ✅ 成功率统计和智能提示
|
||||
- ✅ 更清晰的配置结构,支持分组注释
|
||||
- ✅ 简化配置解析逻辑
|
||||
- ✅ 保持向后兼容(仍支持旧版INI格式)
|
||||
|
||||
### v1.x
|
||||
- 使用 INI 格式配置文件
|
||||
- 需要 `ip1=`, `ip2=` 等前缀
|
||||
- 无实时进度显示
|
||||
|
||||
## 💡 优势
|
||||
|
||||
1. **更易读**:YAML格式天然支持层级和注释
|
||||
2. **易维护**:添加/删除IP无需重新编号
|
||||
3. **更灵活**:支持分组管理不同类型的服务器
|
||||
4. **标准化**:YAML是业界标准的配置格式
|
||||
5. **可视化**:彩色输出和实时进度提升用户体验
|
||||
6. **智能化**:自动分析系统健康状态
|
||||
|
||||
## ⚠️ 注意事项
|
||||
|
||||
- 首次运行时会自动创建示例配置文件
|
||||
- 如果 `config.yml` 不存在,会尝试加载旧的 `config.ini`
|
||||
- 建议在修改配置后备份原文件
|
||||
- Windows环境下colorama可能需要管理员权限才能正常显示颜色
|
||||
|
||||
## 🛠️ 故障排除
|
||||
|
||||
### 问题1:颜色不显示
|
||||
**解决**:确保安装了colorama库
|
||||
```bash
|
||||
pip install colorama
|
||||
```
|
||||
|
||||
### 问题2:配置文件加载失败
|
||||
**解决**:检查YAML格式是否正确,可以使用在线YAML验证器
|
||||
|
||||
### 问题3:检测速度慢
|
||||
**解决**:增加max_workers参数值,但注意不要超过50
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
[IP_LIST]
|
||||
# 每行一个IP地址,可选添加注释(使用 # 或 , 分隔)
|
||||
10.10.14.220 # Oracle Rac节点1
|
||||
10.10.14.222 # Oracle Rac存储节点(IP冲突,待调整)
|
||||
10.10.14.221 # Oracle Rac节点2(IP冲突,待调整)
|
||||
10.10.30.161 # 天融信3.5.6(DELL R730)(88G+1.7T)
|
||||
10.10.30.215 # H3C CAS7.0(X10主板)(128G+3.5T)
|
||||
10.10.14.203 # Cache 2016(X9主板)(32G+2.5T)
|
||||
10.10.13.80 # 云宏8.2.2(DELL R710)(64G+480GB SSD+11TB)
|
||||
10.10.30.45 # 浪潮虚拟化(DELL R710)(112G+4TB+24T)
|
||||
10.10.13.2 # Linux代理(Super Server主板)(128G+14TB+64TB)
|
||||
10.10.30.218 # Minio(X9主板)(32G+48T)后期可考虑报废
|
||||
10.10.30.230 # 黑方规格三,695测试黑方(128G+48T)
|
||||
10.10.30.87 # Win 2012 R2环境(X9主板)(64G+2T)
|
||||
10.10.9.32 # H3C UIS8.0(X10主板)(128G+15T)
|
||||
10.10.14.205 # Linux(X9主板)(64G+4T)
|
||||
10.10.30.20 # SmartX 5.1.4(DELL 730XD)(155G+14T)
|
||||
10.10.5.98 # HPUX
|
||||
10.10.13.30 # ESXi 6.5:计划搭建OpenStack环境
|
||||
10.10.13.31 # ESXi 7.0:计划搭建OpenStack环境
|
||||
10.10.13.32 # ESXi 7.0:计划搭建OpenStack环境
|
||||
10.10.13.33 # 云祺6.0.5
|
||||
10.10.50.232 # Windows2012R2
|
||||
10.10.5.60 # SPARC Solaris
|
||||
10.10.5.31 # SPARC Solaris
|
||||
10.10.13.101 # 曙光云Xstor分布式存储
|
||||
10.10.13.102 # 曙光云本地存储环境
|
||||
10.10.13.103 # 曙光云ceph分布式存储
|
||||
10.10.13.20 # 曙光云ceph分布式存储
|
||||
10.10.14.218 # 深信服桌面云5.4版本,可搭建其他环境
|
||||
10.10.0.100 # 小机 AIX
|
||||
10.10.30.21 # 黑方规格二,SmartX 5.1.4(64G+14T)
|
||||
10.10.25.27 # 华为虚拟化(DELL 710)(144G+11T)
|
||||
10.10.30.22 # SmartX 5.1.4版本(DELL 710)(112G+14T)
|
||||
10.10.14.207 # 华为虚拟化8.3.0(X10主板)(64G+13T)
|
||||
|
||||
# 可选参数
|
||||
count = 2
|
||||
timeout = 2
|
||||
max_workers = 3
|
||||
|
|
@ -0,0 +1,54 @@
|
|||
# Ping多多配置文件 - YAML格式
|
||||
# 每行一个IP地址,冒号后添加注释(可选)
|
||||
|
||||
IP_LIST:
|
||||
# Oracle Rac集群
|
||||
10.10.14.220: "Oracle Rac节点1"
|
||||
10.10.14.222: "Oracle Rac存储节点(IP冲突,待调整)"
|
||||
10.10.14.221: "Oracle Rac节点2(IP冲突,待调整)"
|
||||
|
||||
# 虚拟化平台
|
||||
10.10.30.161: "天融信3.5.6(DELL R730)(88G+1.7T)"
|
||||
10.10.30.215: "H3C CAS7.0(X10主板)(128G+3.5T)"
|
||||
10.10.13.80: "云宏8.2.2(DELL R710)(64G+480GB SSD+11TB)"
|
||||
10.10.30.45: "浪潮虚拟化(DELL R710)(112G+4TB+24T)"
|
||||
10.10.9.32: "H3C UIS8.0(X10主板)(128G+15T)"
|
||||
10.10.14.218: "深信服桌面云5.4版本,可搭建其他环境"
|
||||
10.10.25.27: "华为虚拟化(DELL 710)(144G+11T)"
|
||||
10.10.14.207: "华为虚拟化8.3.0(X10主板)(64G+13T)"
|
||||
|
||||
# 存储系统
|
||||
10.10.14.203: "Cache 2016(X9主板)(32G+2.5T)"
|
||||
10.10.13.2: "Linux代理(Super Server主板)(128G+14TB+64TB)"
|
||||
10.10.30.218: "Minio(X9主板)(32G+48T)后期可考虑报废"
|
||||
10.10.13.101: "曙光云Xstor分布式存储"
|
||||
10.10.13.102: "曙光云本地存储环境"
|
||||
10.10.13.103: "曙光云ceph分布式存储"
|
||||
10.10.13.20: "曙光云ceph分布式存储"
|
||||
|
||||
# 测试环境
|
||||
10.10.30.230: "黑方规格三,695测试黑方(128G+48T)"
|
||||
10.10.30.87: "Win 2012 R2环境(X9主板)(64G+2T)"
|
||||
10.10.30.20: "SmartX 5.1.4(DELL 730XD)(155G+14T)"
|
||||
10.10.30.21: "黑方规格二,SmartX 5.1.4(64G+14T)"
|
||||
10.10.30.22: "SmartX 5.1.4版本(DELL 710)(112G+14T)"
|
||||
10.10.14.205: "vastbase"
|
||||
|
||||
# ESXi环境
|
||||
10.10.13.30: "ESXi 6.5:计划搭建OpenStack环境"
|
||||
10.10.13.31: "ESXi 7.0:计划搭建OpenStack环境"
|
||||
10.10.13.32: "ESXi 7.0:计划搭建OpenStack环境"
|
||||
|
||||
# 其他系统
|
||||
10.10.13.33: "云祺6.0.5"
|
||||
10.10.50.232: "Windows2012R2"
|
||||
10.10.5.98: "HPUX"
|
||||
10.10.5.60: "SPARC Solaris"
|
||||
10.10.5.31: "SPARC Solaris"
|
||||
10.10.0.100: "小机 AIX"
|
||||
|
||||
# 可选参数配置
|
||||
SETTINGS:
|
||||
count: 2 # ping次数
|
||||
timeout: 2 # 超时时间(秒)
|
||||
max_workers: 3 # 并发线程数
|
||||
|
|
@ -0,0 +1,170 @@
|
|||
{
|
||||
"timestamp": "2026-06-18 16:34:14",
|
||||
"results": [
|
||||
{
|
||||
"ip": "10.10.14.222",
|
||||
"status": "Fail",
|
||||
"comment": "Oracle Rac存储节点(IP冲突,待调整)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.14.221",
|
||||
"status": "Fail",
|
||||
"comment": "Oracle Rac节点2(IP冲突,待调整)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.14.220",
|
||||
"status": "Fail",
|
||||
"comment": "Oracle Rac节点1"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.30.215",
|
||||
"status": "Pass",
|
||||
"comment": "H3C CAS7.0(X10主板)(128G+3.5T)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.30.161",
|
||||
"status": "Fail",
|
||||
"comment": "天融信3.5.6(DELL R730)(88G+1.7T)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.13.80",
|
||||
"status": "Fail",
|
||||
"comment": "云宏8.2.2(DELL R710)(64G+480GB SSD+11TB)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.9.32",
|
||||
"status": "Pass",
|
||||
"comment": "H3C UIS8.0(X10主板)(128G+15T)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.30.45",
|
||||
"status": "Fail",
|
||||
"comment": "浪潮虚拟化(DELL R710)(112G+4TB+24T)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.14.207",
|
||||
"status": "Pass",
|
||||
"comment": "华为虚拟化8.3.0(X10主板)(64G+13T)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.14.218",
|
||||
"status": "Fail",
|
||||
"comment": "深信服桌面云5.4版本,可搭建其他环境"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.25.27",
|
||||
"status": "Fail",
|
||||
"comment": "华为虚拟化(DELL 710)(144G+11T)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.14.203",
|
||||
"status": "Fail",
|
||||
"comment": "Cache 2016(X9主板)(32G+2.5T)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.13.2",
|
||||
"status": "Fail",
|
||||
"comment": "Linux代理(Super Server主板)(128G+14TB+64TB)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.30.218",
|
||||
"status": "Fail",
|
||||
"comment": "Minio(X9主板)(32G+48T)后期可考虑报废"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.13.101",
|
||||
"status": "Fail",
|
||||
"comment": "曙光云Xstor分布式存储"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.13.102",
|
||||
"status": "Fail",
|
||||
"comment": "曙光云本地存储环境"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.13.103",
|
||||
"status": "Fail",
|
||||
"comment": "曙光云ceph分布式存储"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.30.230",
|
||||
"status": "Pass",
|
||||
"comment": "黑方规格三,695测试黑方(128G+48T)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.13.20",
|
||||
"status": "Fail",
|
||||
"comment": "曙光云ceph分布式存储"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.30.87",
|
||||
"status": "Fail",
|
||||
"comment": "Win 2012 R2环境(X9主板)(64G+2T)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.30.20",
|
||||
"status": "Fail",
|
||||
"comment": "SmartX 5.1.4(DELL 730XD)(155G+14T)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.30.21",
|
||||
"status": "Fail",
|
||||
"comment": "黑方规格二,SmartX 5.1.4(64G+14T)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.14.205",
|
||||
"status": "Pass",
|
||||
"comment": "vastbase"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.30.22",
|
||||
"status": "Fail",
|
||||
"comment": "SmartX 5.1.4版本(DELL 710)(112G+14T)"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.13.30",
|
||||
"status": "Fail",
|
||||
"comment": "ESXi 6.5:计划搭建OpenStack环境"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.13.31",
|
||||
"status": "Fail",
|
||||
"comment": "ESXi 7.0:计划搭建OpenStack环境"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.13.32",
|
||||
"status": "Fail",
|
||||
"comment": "ESXi 7.0:计划搭建OpenStack环境"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.13.33",
|
||||
"status": "Fail",
|
||||
"comment": "云祺6.0.5"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.50.232",
|
||||
"status": "Fail",
|
||||
"comment": "Windows2012R2"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.5.98",
|
||||
"status": "Fail",
|
||||
"comment": "HPUX"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.5.60",
|
||||
"status": "Fail",
|
||||
"comment": "SPARC Solaris"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.5.31",
|
||||
"status": "Fail",
|
||||
"comment": "SPARC Solaris"
|
||||
},
|
||||
{
|
||||
"ip": "10.10.0.100",
|
||||
"status": "Fail",
|
||||
"comment": "小机 AIX"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
#encoding=utf-8
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
class FileCopier:
|
||||
def __init__(self):
|
||||
self.path = Path.cwd()
|
||||
self.file_path = self.path / 'file.txt'
|
||||
self.dir_list = list(range(1, 61))
|
||||
|
||||
def init_dir(self):
|
||||
"""初始化目录"""
|
||||
for dir_num in self.dir_list:
|
||||
dir_path = self.path / str(dir_num)
|
||||
if dir_path.exists():
|
||||
shutil.rmtree(dir_path)
|
||||
dir_path.mkdir()
|
||||
print("目录初始化完成")
|
||||
|
||||
def _copy_files(self, file_list):
|
||||
"""复制文件到目录中"""
|
||||
if not self.file_path.exists():
|
||||
raise FileNotFoundError(f"源文件 {self.file_path} 不存在")
|
||||
|
||||
file_dir_map = dict(zip(file_list, self.dir_list))
|
||||
success_count = 0
|
||||
|
||||
for file_num, dir_num in file_dir_map.items():
|
||||
new_name = f'file_{file_num}.txt'
|
||||
target_path = self.path / str(dir_num) / new_name
|
||||
try:
|
||||
shutil.copyfile(self.file_path, target_path)
|
||||
success_count += 1
|
||||
except Exception as e:
|
||||
print(f"复制文件 {file_num} 到目录 {dir_num} 失败: {e}")
|
||||
|
||||
print(f"成功复制 {success_count} 个文件")
|
||||
|
||||
def copy_odd(self):
|
||||
"""复制奇数文件"""
|
||||
file_list = list(range(1, 121, 2))
|
||||
print(f"开始复制 {len(file_list)} 个奇数文件...")
|
||||
self._copy_files(file_list)
|
||||
|
||||
def copy_even(self):
|
||||
"""复制偶数文件"""
|
||||
file_list = list(range(2, 121, 2))
|
||||
print(f"开始复制 {len(file_list)} 个偶数文件...")
|
||||
self._copy_files(file_list)
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
if len(sys.argv) != 2:
|
||||
print("用法: python copy_files.py [init|js|os]")
|
||||
print(" init - 初始化目录")
|
||||
print(" js - 复制奇数文件")
|
||||
print(" os - 复制偶数文件")
|
||||
sys.exit(1)
|
||||
|
||||
command = sys.argv[1].lower()
|
||||
copier = FileCopier()
|
||||
|
||||
try:
|
||||
if command == 'init':
|
||||
copier.init_dir()
|
||||
elif command == 'js':
|
||||
copier.copy_odd()
|
||||
elif command == 'os':
|
||||
copier.copy_even()
|
||||
else:
|
||||
print(f"错误: 未知命令 '{command}'")
|
||||
print("请使用 init, js 或 os")
|
||||
sys.exit(1)
|
||||
except Exception as e:
|
||||
print(f"执行失败: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
文件复制工具:将源文件按奇/偶数编号复制到多个目录中。
|
||||
|
||||
用法:
|
||||
python copy_files.py init # 初始化目录
|
||||
python copy_files.py odd # 复制奇数编号文件 (1,3,5,...,119)
|
||||
python copy_files.py even # 复制偶数编号文件 (2,4,6,...,120)
|
||||
python copy_files.py clean # 清理生成的目录和源文件
|
||||
|
||||
兼容旧命令: js(=odd) / os(=even)
|
||||
"""
|
||||
import argparse
|
||||
import shutil
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
# ===== 配置参数(可按需调整)=====
|
||||
TOTAL_FILES = 120 # 文件总数 (1~120)
|
||||
TOTAL_DIRS = 60 # 目标目录数 (1~60)
|
||||
SOURCE_FILE = 'file.txt' # 源文件名
|
||||
SOURCE_FILE_SIZE = 1*1024 * 1024 # 源文件不存在时自动生成的大小(字节,默认 1MB)
|
||||
|
||||
|
||||
class FileCopier:
|
||||
def __init__(self, ensure_source=True):
|
||||
self.path = Path.cwd()
|
||||
self.file_path = self.path / SOURCE_FILE
|
||||
self.dir_list = list(range(1, TOTAL_DIRS + 1))
|
||||
# clean 等清理操作不需要生成源文件
|
||||
if ensure_source:
|
||||
self._ensure_source_file()
|
||||
|
||||
def _ensure_source_file(self):
|
||||
"""确保源文件存在:不存在则自动生成一个指定大小的占位文件。"""
|
||||
if self.file_path.exists():
|
||||
return
|
||||
with self.file_path.open('wb') as f:
|
||||
# 一次性写入全零字节,速度快且兼容大文件场景
|
||||
f.write(b'\0' * SOURCE_FILE_SIZE)
|
||||
print(f"源文件不存在,已自动生成 {self.file_path.name}({SOURCE_FILE_SIZE // 1024}KB)")
|
||||
|
||||
def init_dir(self):
|
||||
"""初始化目录:删除已存在的同名目录后重新创建。"""
|
||||
for dir_num in self.dir_list:
|
||||
dir_path = self.path / str(dir_num)
|
||||
if dir_path.exists():
|
||||
shutil.rmtree(dir_path)
|
||||
dir_path.mkdir(parents=True)
|
||||
print(f"已初始化 {len(self.dir_list)} 个目录")
|
||||
|
||||
def clean(self):
|
||||
"""清理:删除所有生成的目录(1~60)及源文件。"""
|
||||
removed_dirs = 0
|
||||
for dir_num in self.dir_list:
|
||||
dir_path = self.path / str(dir_num)
|
||||
if dir_path.exists():
|
||||
shutil.rmtree(dir_path)
|
||||
removed_dirs += 1
|
||||
# 删除源文件
|
||||
if self.file_path.exists():
|
||||
self.file_path.unlink()
|
||||
print(f"已删除源文件 {self.file_path.name}")
|
||||
print(f"已清理 {removed_dirs} 个目录")
|
||||
|
||||
def _copy_files(self, file_list):
|
||||
"""将源文件复制到各目录,文件名格式为 file_<编号>.txt。"""
|
||||
# 源文件已在 __init__ 时确保存在;此处再校验以防外部删除
|
||||
if not self.file_path.exists():
|
||||
raise FileNotFoundError(f"源文件 {self.file_path} 不存在")
|
||||
|
||||
# 按顺序将文件分配到目录:file_list[i] -> dir_list[i]
|
||||
file_dir_map = dict(zip(file_list, self.dir_list))
|
||||
success_count = 0
|
||||
|
||||
for file_num, dir_num in file_dir_map.items():
|
||||
target_path = self.path / str(dir_num) / f"file_{file_num}.txt"
|
||||
try:
|
||||
shutil.copyfile(self.file_path, target_path)
|
||||
success_count += 1
|
||||
except Exception as e:
|
||||
print(f"复制文件 {file_num} 到目录 {dir_num} 失败: {e}")
|
||||
|
||||
print(f"成功复制 {success_count}/{len(file_list)} 个文件")
|
||||
|
||||
def copy_odd(self):
|
||||
"""复制奇数编号文件 (1,3,5,...,119)。"""
|
||||
file_list = list(range(1, TOTAL_FILES + 1, 2))
|
||||
print(f"开始复制 {len(file_list)} 个奇数文件...")
|
||||
self._copy_files(file_list)
|
||||
|
||||
def copy_even(self):
|
||||
"""复制偶数编号文件 (2,4,6,...,120)。"""
|
||||
file_list = list(range(2, TOTAL_FILES + 1, 2))
|
||||
print(f"开始复制 {len(file_list)} 个偶数文件...")
|
||||
self._copy_files(file_list)
|
||||
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description='文件复制工具:按奇/偶数编号复制源文件到多个目录',
|
||||
formatter_class=argparse.RawDescriptionHelpFormatter,
|
||||
epilog="兼容旧命令: js(=odd 奇数) / os(=even 偶数)"
|
||||
)
|
||||
parser.add_argument(
|
||||
'command',
|
||||
choices=['init', 'odd', 'even', 'clean', 'js', 'os'],
|
||||
help='init: 初始化目录; odd/js: 复制奇数文件; even/os: 复制偶数文件; clean: 清理目录和源文件'
|
||||
)
|
||||
args = parser.parse_args()
|
||||
|
||||
# clean 命令不需要自动生成源文件
|
||||
copier = FileCopier(ensure_source=(args.command != 'clean'))
|
||||
try:
|
||||
if args.command == 'init':
|
||||
copier.init_dir()
|
||||
elif args.command in ('odd', 'js'):
|
||||
copier.copy_odd()
|
||||
elif args.command in ('even', 'os'):
|
||||
copier.copy_even()
|
||||
elif args.command == 'clean':
|
||||
copier.clean()
|
||||
except Exception as e:
|
||||
print(f"执行失败: {e}")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
Loading…
Reference in New Issue