-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMoveToParentFolder.py
More file actions
140 lines (106 loc) · 4.49 KB
/
Copy pathMoveToParentFolder.py
File metadata and controls
140 lines (106 loc) · 4.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# 请帮我写个中文的 Python 脚本,批注也是中文:
# 在脚本开始前询问我源文件位置,默认为"d:\Studios\Folders\Downloads\"。
# 将文件夹下的所有文件夹内(包括子文件夹)的文件上移到文件的父文件夹中。
import signal
import shutil
import sys
from pathlib import Path
# ==================== 全局配置 ====================
DEFAULT_SOURCE_DIR = Path(r"d:\Studios\Folders\Downloads")
_quit_requested = False # Ctrl+Q 中断标志
# --- 消息常量 ---
MSG_PROMPT_SOURCE_DIR = "请输入源文件夹位置:"
MSG_ERR_INVALID_PATH = "提供的路径不是有效的文件夹路径,请检查后重试。"
MSG_FILE_MOVED = "文件已移动: {} -> {}"
MSG_FILE_MOVE_ERROR = "无法移动文件: {} -> {}. 错误: {}"
MSG_DELETED_EMPTY_FOLDER = "已删除空文件夹: {}"
MSG_DELETE_FOLDER_ERROR = "无法删除文件夹: {}. 错误: {}"
MSG_MOVE_COMPLETE = "所有文件已移动完成,共移动 {} 个文件。"
MSG_INPUT_DEFAULT_HINT = " (默认: {}): "
MSG_INTERRUPTED = "\n\n用户中断程序,已退出。"
MSG_ERROR = "\n程序运行出错: {}"
MSG_EXIT = "\n按回车键退出..."
# ==================== 辅助函数 ====================
def get_input_with_default(prompt_text: str, default_value: str) -> str:
"""获取带默认值的用户输入。"""
user_input = input(f"{prompt_text}{MSG_INPUT_DEFAULT_HINT.format(default_value)}").strip()
return user_input if user_input else str(default_value)
def move_files_to_parent_directory(source_dir: Path) -> int:
"""
将文件夹内(包括子文件夹)的所有文件上移到其父文件夹。
从最深子目录向浅层处理,处理完后删除空文件夹。
返回移动的文件数量。
"""
# 收集所有子目录,按深度降序(最深优先)
all_dirs = [p for p in source_dir.rglob("*") if p.is_dir()]
all_dirs.sort(key=lambda p: len(p.parts), reverse=True)
moved_count = 0
for dir_path in all_dirs:
for entry in dir_path.iterdir():
if not entry.is_file():
continue
parent_dir = dir_path.parent
file_stem = entry.stem
file_suffix = entry.suffix
# 目标路径(父文件夹下同名文件)
new_path = parent_dir / entry.name
# 如果目标已存在,加 _moved 后缀避免冲突
while new_path.exists():
file_stem = f"{file_stem}_moved"
new_path = parent_dir / f"{file_stem}{file_suffix}"
try:
shutil.move(str(entry), str(new_path))
print(MSG_FILE_MOVED.format(entry, new_path))
moved_count += 1
except OSError as e:
print(MSG_FILE_MOVE_ERROR.format(entry, new_path, e))
# 如果当前文件夹已空,删除
try:
remaining = list(dir_path.iterdir())
if not remaining:
dir_path.rmdir()
print(MSG_DELETED_EMPTY_FOLDER.format(dir_path))
except OSError as e:
print(MSG_DELETE_FOLDER_ERROR.format(dir_path, e))
return moved_count
# ==================== 主程序 ====================
# ==================== 中断处理 ====================
def _on_quit_signal(signum, frame):
global _quit_requested
_quit_requested = True
raise KeyboardInterrupt()
def _init_quit_handler():
if hasattr(signal, "SIGQUIT"):
signal.signal(signal.SIGQUIT, _on_quit_signal)
def _check_quit() -> bool:
global _quit_requested
if sys.platform == "win32":
try:
import msvcrt
while msvcrt.kbhit():
if msvcrt.getch() == b"\x11":
_quit_requested = True
except Exception:
pass
return _quit_requested
def main() -> None:
"""主函数:处理用户输入并调用文件移动函数。"""
source_str = get_input_with_default(
MSG_PROMPT_SOURCE_DIR, str(DEFAULT_SOURCE_DIR))
source_dir = Path(source_str)
if not source_dir.is_dir():
print(MSG_ERR_INVALID_PATH)
return
count = move_files_to_parent_directory(source_dir)
print(MSG_MOVE_COMPLETE.format(count))
# ==================== 程序入口 ====================
if __name__ == "__main__":
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
try:
main()
except KeyboardInterrupt:
print(MSG_INTERRUPTED)
except Exception as e:
print(MSG_ERROR.format(e))
finally:
input(MSG_EXIT)