分かりやすいようにとフォルダごとに分けて保存したのに、細かくフォルダを分けすぎて逆に分かりにくくなったという経験はないだろうか。細かく分け過ぎることで、何度もフォルダをクリックしないとそのファイルにたどり着けないので不便だ。そこでPythonを使って深いフォルダ階層のファイルを1つのフォルダに移動するプログラムを作ってみよう。
プログラムを実行する前に考えよう
深いフォルダ階層にある大量のファイルを1つのフォルダに移動することを考えた時、いくつか考えるべきことがある。プログラムを実行する前に考慮すべき、以下の点をよく考えよう。まずは箇条書きにしてみた。
- プログラム実行前にバックアップする
- フォルダ名を移動後のファイル名に加えて同名ファイルの衝突を避ける
- フォルダの中にあるフォルダ(サブフォルダ)のファイルも処理する
一つずつ考慮していこう。そもそも、大量のファイルを何も考えずに移動させると失敗することが多い。そのため、ファイルの移動が目的であったとしても、まずは、プログラムの実行前に入念にバックアップをとっておこう。Pythonのプログラムでは移動処理も可能だが、ここでは移動させるのではなくファイルコピーをすることにしよう。
それから複数フォルダに分かれたファイルを1つのフォルダにまとめようと思うと必ず起きるのがファイル名の衝突だ。例えば「2022-12/給料明細.xlsx」と「2022-11/給料明細.xlsx」というファイルがあったとしよう。(なお「/」はLinuxやmacOSでフォルダの区切り記号である。)これを1つのフォルダにまとめるなら、どちらもファイル名が「給料明細.xlsx」なので何も考えずにコピーするなら既にコピーしたファイルを後からコピーするファイルで上書きしてしまう。そのため、移動後のファイル名をどうするかを先に考えておく必要がある。この場合、「2022-12_給料明細.xlsx」「2022-11_給料明細.xlsx」のようにフォルダ名を移動先のファイル名に加えることができるだろう。
また、ファイルの一覧を取得する場合、指定のフォルダだけでなく、そのサブフォルダ以下にあるファイルも取得する必要がある。
サブフォルダ以下の複数ファイルを1つのフォルダにコピーするプログラム
上記の点を踏まえてプログラムを作ってみよう。
import tkinter.messagebox as msg
import tkinter.filedialog as dlg
import os, shutil, re
def main():
# フォルダの選択 --- (*1)
msg.showinfo(message='コピー元フォルダを選択してください')
source_dir = dlg.askdirectory()
if source_dir == '': return
msg.showinfo(message='コピー先フォルダを選択してください')
target_dir = dlg.askdirectory()
if target_dir == '': return
print('source=',type(source_dir))
print('taget=', target_dir)
# ファイルの一覧を列挙 --- (*2)
for cur_dir, dirs, files in os.walk(source_dir):
print(f'[{cur_dir}]')
for f in files:
# 保存ファイル名を決定 --- (*3)
fdir = cur_dir[len(source_dir)+1:] + '_' + f
savename = re.sub(r'[\\\/]', '_', fdir, 0)
print('-', savename)
try:
# ファイルのコピー --- (*4)
cp_from = os.path.join(cur_dir, f)
cp_to = os.path.join(target_dir, savename)
shutil.copy2(cp_from, cp_to)
except:
print('[ERROR]', cp_from)
if __name__ == '__main__':
main()
上記のプログラムを「copy_files.py」という名前で保存しよう。そして、次のコマンドを実行しよう。あるいは、IDLEを起動してプログラムを実行しよう。