【Python】指定の場所にあるデータを指定の場所に移動させるプログラム
パソコンを使っていると指定の場所から指定の場所へデータを移動させることが度々あります。全てのデータを選択して、マウスで移動先へドラッグ&ドロップするという単純な作業ではありますが、1~2回ならともかく複数回になるとその分だけ同じ動作を繰り返すことになります。
その時だけで終わればいいのですが、その作業を今後も度々行うとなると意外に面倒に感じるようになるんですよね。移動元のフォルダを開いて、移動先のフォルダを開いて、該当するファイルを選んで、うんぬんかんぬん。これからもこの作業を延々やり続けるのかと、考えただけでも…
というわけで、プログラムで何とかできないかと思って、それ専用のプログラムをプログラム言語のPythonで作ってみました。
※このプログラムはプログラム言語『Python』のインストール、および各モジュールが使用可能な状態であることが前提となります。
当サイトではアフィリエイト広告を利用しています。
リンクや画像に広告が含まれている場合があることを予めご了承ください。
プログラムの特徴
- 移動場所はいくらでも増やせる
- パスが確認できないものは移動をせず、確認できたものだけ移動する
- 移動した経過、エラーの状況を表示する
- 処理後はEnterキーで画面を閉じる
- 元データは移動後に消失する(別々のドライブ間であっても)
プログラムの使い方
下記のコードをpyファイルにコピペする
import os
import shutil
def data_move(before, after):
for file in os.listdir(before):
if os.path.exists(before) and os.path.exists(after):
shutil.move(before+file, after+file)
print(before+file, 'を移動しました')
else:
print(before+file, 'の移動に失敗')
try:
data_move('C:\\パス1\\', 'C:\\パス2\\')
except:
print('【】の取得に失敗')
input(
'''
Enterキーを押してください
'''
)
移動前フォルダの絶対パスを入力
data_move('C:\\パス1\\', 'C:\\パス2\\')
tryの下にあるdata_moveの『C:\\パス1\\』を削除して、移動したいデータがあるフォルダの絶対パスを入力してください。間違ってシングルクォーテーション(´)を消さないでください。正しく機能しなくなります。フォルダ間の区切りはバックスラッシュ(\\)または半角¥マーク2つです。最終的にデータが格納されているフォルダ名の後にも2つ必要なので忘れないように。
移動先フォルダの絶対パスを入力
data_move('C:\\パス1\\', 'C:\\パス2\\')
tryの下にあるdata_moveの『C:\\パス2\\」を削除して、移動先のフォルダの絶対パスを入力してください。それ以外は移動前のフォルダと同じようにしてください。
移動前と移動先は絶対パスを推奨しています。このプログラムの本体(.py)がある場所から相対的にある場所への相対パスを指定してもかまいません。その場合はプログラム本体とフォルダの場所の位置関係をしっかり考えて指定してください。絶対パスでの指定の方がプログラム本体の場所に左右されずに実行できるので確実だとは思います。
エラー時の詳細がわかるようにフォルダの名前を入力
print('【 】の取得に失敗')
exceptの下にあるprintという記述の中の【】の中に移動前のフォルダのフォルダ名を書き加えます。【フォルダA】といった具体です。移動前のフォルダのパスが正しく入力されなかった時にエラーが発生するのですが、その時にテキストを表示するように設定しています。エラーが発生した時に「【○○】の取得に失敗」と表示されるのですが、○○がどこなのかがわからないとどこを修正していいかわからなくなるので、該当部分がどこなのかをわかりやすくするために記述します。
サンプルコードで見てみよう
修正する部分のサンプルを書いてみたので参考にしてください。
try:
data_move('C:\\パス1\\', 'C:\\パス2\\')
except:
print('【】の取得に失敗')
↓↓↓
try:
data_move('C:\\sampleA\\', 'C:\\sampleB\\')
except:
print('【sampleA】の取得に失敗')
try:
data_move('D:\\sampleC\\testA\\', 'F:\\sampleD\\testB\\')
except:
print('【testA】の取得に失敗')
移動させたいデータがあるフォルダが『c:\\sampleA\\』で、移動先のフォルダが『c:\\sampleB\\』となっています。Cドライブの『sampleA』というフォルダの中の全データを、同じくCドライブの『sampleB』というフォルダに移動させる、となります。実際にフォルダが存在した場合はデータが移動されます。
『sampleA』のフォルダ自体がない場合、または『c:\\samA\\』といったパスの指定が間違っていた場合は、「【sampleA】の取得に失敗」と表示されます。【】の中の表示を見れば、どこのフォルダが間違っているかが一目瞭然です。ここはすぐわかるのであればテキストは何でもかまいませんが、フォルダの名前を入力しておくとわかりやすいかと思います。
移動させる場所を増やしたい場合
tryとexceptをセットでコピペして、該当部分を編集してください。サンプルコードの2つ目のtry・exceptがそれにあたります。こちらの場合は、DドライブのsampleCの中にあるtestAというフォルダの中にあるデータを、FドライブのsampleDの中にあるtestBというフォルダへ移動する、となります。このように好きな場所から好きな場所へ、いくらでも増やすことができます。
編集内容を保存後、pyファイルをダブルクリック
- 正常:○○を移動しました
- 移動元が間違っている:【○○】の取得に失敗
- 移動先が間違っている:○○の移動に失敗
全て正常ならデータの移動が滞りなく完了しているはずです。失敗したら移動元または移動先が間違っているか、プログラム本体を誤って手を加えてしまったかのどれかになるでしょう。
プログラムの処理が終わったらEnterキーを押して画面を閉じる
このプログラムを実行するとコマンドプロンプト(pyファイル実行時に出てきた黒い画面)が立ち上がります。移動処理が済んだら「Enterキーを押してください」と出るので、そこでEnterキーを押せば画面が閉じて完了、という仕様になっています。
基本的に処理だけ書いたプログラムを実行したら、コマンドプロンプトが立ち上がって処理を始め、完了後は自動で画面が閉じて終了になります。しかし、それだとちゃんと移動したのか、エラーは出なかったかなどを確認できなくなります。結果だけ残って過程がどうなっているかがわからなくなります。
ちゃんと移動したかどうかを確認するためにも、コマンドプロンプトを自動で閉じるのではなく、Enterキーを押して閉じるようにしました。画面上には処理した内容が表示されているはずなので、不備がないか確認してください。問題なければEnterキーを押せば画面が閉じます。
ちなみに、Enterキーをいちいち押すのは面倒だ、という人は下記の部分を削除して下さい。
input(
'''
Enterキーを押してください
'''
)
ここは最後のキーを押す作業を司っている処理です。ここを削除すれば自動で閉じるようになります。
以上で使い方の説明は終了です。
移動した元データは消える
使っているコード(shutil.move)の仕様とでも言いましょうか、移動したデータの元データは消えてしまいます。
普通のパソコン操作なら、同じドライブ間でデータを移動させれば、それは単なる移動になります。移動元からデータが消えて、移動先にデータが移る。
別々のドライブ間でデータを移動させれば、それはコピー作業となります。元のドライブからデータは消えず、別のドライブに同じデータが複製される。つまり、両方に同じデータが存在する状態になります。
このプログラムにおいての移動は前者、すなわち移動した元データは消えて移動先に移る、という働きになります。これはドライブが別々であっても変わりません。移動元からデータが消えて、移動先にデータが移ります。
別々のドライブ間でデータが消えないことを利用して、後からその元データを編集したいと考えているのならそうはなりませんよ、と言いたいわけです。
後で編集するならコピーをとっておこう
これは私の個人的なやり方において2度手間が生じた経験から言うのですが、私は買ってきた本や雑誌はスキャンして電子書籍にしています。本ごとにフォルダを作って、そのフォルダを一旦別のHDDに保存版としてコピーします。基本的なパソコン操作なら別のドライブに移動させただけなら元のデータは消えません。だから、一旦保存した後は元のデータに編集を加えてタブレット版にして読むようにしているんです。
この時にですね、今まで手動でやってきたことをこのプログラムで代用したわけです。ダブルクリックでぱぱっと移動できるので。
しかしですね、このプログラムでの移動は先ほども言ったように元データが消えてしまいます。ゴミ箱に行くわけでもなく、消失してしまいます。別々のドライブ間であっても。
となると、後で編集しようとしていたデータが消えてしまうので、移動先から探して再度コピーして、という手間が生じてしまったわけです。効率化を目論見、単なるデータの移動でさえもプログラム化したというのに、結果的に探す手間が増えるという落とし穴にハマってしまったんです。本のスキャンはまとまった数になってから行うので、その元データが消えてしまうと探す作業も増えてしまい、完全に2度手間。
単純な移動だけが目的なら問題ないですが、後でデータに何かしたいなら事前にどこか別の場所にコピーを取っておかないといちいち探す手間が発生します。このプログラムを実行する時はそういった作業を終えてから、最終的に移動するだけになってから実行してください。
まあ、そんなことになるのは自分だけのような気がしなくもないですが…。一応、この点を理解しておいてくださいね。
自動化で効率化
わざわざPythonでなくても移動くらいならフリーソフトでいくらでも対応できるとは思います。ただ、移動以外にもいろいろ手を加えていける自由度がプログラムにはあります。
簡易的なプログラムではありますが、これを作ったおかげでプログラムをダブルクリックするだけでファイルの移動ができるようになりました。しかも、今後移動場所が増えても移動元と移動先の記述を増やすだけで対応できます。
1つ1つはなんてことない作業でも、何回もやるとなると手間がかかります。自動化できる所は積極的にやっておくと効率も上がって結果的に楽になります。どんな些細なことでも1度見直してみてはいかがでしょう。
プログラムの各部説明
ここからの説明は自分用とでも言いましょうか、どういった考えの元でプログラムを書いたかを残しておくためのものです。プログラムに興味がない人は読み飛ばしていただいてかまいません。
モジュールの読み込み
モジュールとはプログラムの部品のことです。プログラムを書く場合、頻繁に使用するコードが出てきます。コードが長くなったりすると、それらを使うたびに何度も1から書いていくのは効率が悪いです。そういうよく使うコードをまとめておき、いつでも手軽に利用できるようにしたものがモジュールです。
モジュールを使うにあたっては事前にそれを取り込む(インポート)必要があります。モジュールのインポートは『import モジュール名』と書きます。
このプログラムでは、ファイルやディレクトリを操作するのに便利な機能を持っているosモジュール、コピーや移動などの便利な機能を持っているshutilモジュールを使って構築しています。
import os
import shutil
これでモジュールを使うことができます。
独自の関数定義:data_move
移動元パス:before
移動先パス:after
移動させるためのプログラム処理は共通化した方が書くコードが少なくて済みます。同じことを何回も書くのは効率が悪く、手間もかかります。同じ処理を繰り返すなら、それらを1つにまとめて、必要な時にそこを通すようにすれば作業効率も上がります。どういう処理をするのかはプログラム次第なので自分で考えて作る必要があります。そうして作ったのがdata_moveです。
data_moveは2つの引数を指定することになっています。
- 第1引数:移動元のフォルダ名
- 第2引数:移動先のフォルダ名
第1引数→第2引数へデータを移動する設定になっているわけです。
移動前と移動後のパスは任意になるので変数を使ってそれぞれ個別に代入するようにしています。移動する前と後という意味でbefore、afterとしています。関数名はそのまんまデータの移動でdata_move。
データはファイルとディレクトリ(フォルダ)、というか指定ディレクトリの中にあるもの全てが対象となります。名称は任意なので好きに決めて大丈夫。変更する場合は該当する部分も全て書き換えないとプログラムが正しく動作しないので注意。
データを移動させるには場所を示すパスと移動するファイル(ディレクトリ)名が必要なので、ここでは絶対パスで指定しています。別ドライブの指定を視野に入れて、絶対パスが無難と判断したからです。
for file in os.listdir(before):
『for 変数 in ~』はプログラムではお馴染みの繰り返し構文です。1つずつ取り出して処理を行う時に使います。取り出したものを変数(任意の単語、ここではfileと命名)に代入して1個ずつ処理を行っていきます。
os.listdir()は、osモジュールの機能です。カッコ内に指定したパスのファイル名とディレクトリ名の両方を取得します。データを移動させるにはそのデータがある場所とどのデータかを示す名称が必要になります。それをこれで取得するわけです。
if os.path.exists(before) and os.path.exists(after):
ifはプログラムではお馴染みの条件分岐の構文です。条件を与えて、それを満たすもの、満たさないものとで分ける働きがあります。
os.path.exists()はosモジュールの機能です。カッコ内で指定しているパスの存在を確認します。指定した場所にファイルまたはディレクトリがあるかないかを判断します。
ifとos.path.exists()を組み合わせることで、指定場所の中にデータが確認できれば条件を満たした場合の処理を、確認できなければ条件を満たしていない場合の処理に分けます。
ちなみにandは条件を複数与えるもので、書かれた前後の記述の両方を満たしているかどうかを判定します。この場合は、before(移動元)とafter(移動先)の両方が確認されてはじめて条件を満たしたことになります。片方だけでは条件を満たしたことにはなりません。移動させる両方の場所の存在を確認しているわけです。
shutil.move(before+file, after+file)
move('移動前の場所+ファイルまたはディレクトリ名', '移動先の場所+ファイルまたはディレクトリ名')を表しています。
先ほども書きましたが、移動には移動前のパスと移動先のパス、ならびに何を移動するのかそのファイルやディレクトリ名が必要です。before(移動元)とafter(移動先)で場所を指定しています。fileというのは、os.listdir()で取得したデータをfor文で1つずつ取り出したものです。ファイルなら拡張子を含めたファイル名、ディレクトリならディレクトリ名が代入されています。
before+fileでどこにある・どのデータか、after+fileでどこへ移動するのかという形を作り、それぞれをmove()にあてはめて移動させています。
ちなみにmove()はファイル(ディレクトリ)名を変更して移動させることもできます。今回の場合は移動前後で名称は変えていません。名称を表すfileをどちらにもそのまま使用していますから。変更させて移動したいなら、afterの方のfileを任意のものに変えれば可能です。まあ、その場合はそれ用に編集する必要はありますけど。
絶対パスで指定
例えば『C:\\sample\\』と書けば、Cドライブのsampleというフォルダ内を表します。バックスラッシュ(\)はコンピュータ上の区切りを表していて、パスを区切る時に用います。
ちなみにバックスラッシュは特殊文字であり、コンピュータ上では扱いに気を付ける必要があります。本来パソコン上ではパスは1本線で区切られますが、プログラム上では特殊文字であるため、そのままで使用すると不具合が発生します。
特殊文字を通常の文字として使用する場合はバックスラッシュを追加して2本にします。つまり、『\\』とすることで、これは『\』と扱って下さい、となるわけです。これを『エスケープ』または『文字をエスケープする』と言います。
よって、パスを指定する際は区切りを『\\』としています。
print(before+file, 'を移動しました')
これはファイル(またはディレクト)の移動が無事に完了した時に表示させるテキストを表しています。プログラムを実行して何も音沙汰がないとちゃんと実行されたかわからなくなります。移動先を確認すればいいだけなのですが、ちゃんと結果を見られるようにした方がわかりやすいので、ここでそれを証明する文を表示させています。
elseとprint(before+file, 'の移動に失敗')
elseは条件分岐ifによって条件が満たされなかった場合に実行する処理を書きます。何もしないのであれば省略可能です。ここでは条件未達成の場合に「○○の移動に失敗」と表示するように設定しています。
for文とif文の順番について
for文→if文の順番にするのは移動前と移動先のどちらのパスが間違っているかを明確にするためです。移動前が間違っていたら「【○○】の取得に失敗」と出て、移動先が間違っていたら「○○の移動に失敗」と出ます。このテキストの違いでどちらのエラーかがわかるようになっています。
if文→for文の順番だった場合、どっちも「【○○】の取得に失敗」と出てしまい、どっちが間違っているのかわからなくなります。よって、for文→if文の順番で書くことになります。
処理結果の表示は個人的な考えのもとで行っているにすぎない
普通に移動用のプログラムを書くと『移動した』という結果だけが残り、途中の経過まではわかりません。それをあえて途中の経過を表示して『見てわかる』ようにしています。味気無さもあってそうしていますが、絶対ではないのでいくらでも変更してしまってOKです。
tryとexcept
エラーが発生する可能性がある場合の記述(try)と、エラーが出た時はどうするかを記述する部分(except)になります。これは個人的な考えのもと、こういうやり方をとっています。
というのも、データを移動する時は『全ての場所がある』と確定しているわけではありません。データによっては外付けHDDに移動させるものもあり、移動先がいつでも存在するという保証がないわけです。
プログラムは移動元および移動先が存在しないと停止してしまいます。1つでも不備があるとエラーになって全て止まってしまうわけです。
とはいえ、移動させたいものが全て揃っている状態であれば何も問題ありませんが、たとえば外付けHDDだけは移動させる必要がない場合はどうしましょう。いちいち外付けHDDを接続しないとプログラムが実行できなくなります。
移動場所が複数ある場合に、外付けHDDの接続が確認されないとプログラムが止まってしまう。外付けへの移動は何もなくて、Cドライブの移動だけしたい、という場合であっても外付けHDDを接続しないとプログラムが実行できない。こういう状態に陥るわけです。
Cドライブ内で移動するなら確実に存在はするでしょう。ですが、たまに外付けHDDに保存する程度の頻度であれば、データも頻繁に移動することはありません。にもかかわらず、Cドライブ間の移動だけをする時でさえも、わざわざ外付けを接続するなんて無駄に手間がかかるとしか言えません。
ですが、普通にプログラムを書いてしまうと存在が確認できないとエラーになって止まってしまう。Cドライブ用と外付け用で専用にプログラムを分けることもできますが、そんなことをせずに存在の確認ができないものを飛ばしてしまって、確認できるものだけやってくれれば話が早い。移動先が存在しないなら移動先の存在が確認できるものだけ移動する方が都合がいいわけです。その都合のいいやり方を実現するためのtryとexceptです。
エラーが発生する可能性がある(場所が存在しないというエラーが前もってわかっている)場合にtryで書きます。tryで書いている記述にエラーが発生した時には、except部分で書かれている記述が実行されます。今回の場合は下記のようなテキストを表示するようになっています。
【○○】の取得に失敗
『移動先が存在しないから移動はできませんよ』という旨を表示しています。
ちなみにtryで書いている部分はエラーになるとそこだけ停止して次の記述に進みます。移動させたい対象フォルダの実行プログラム(data_move)を別々に書いておき、それぞれにtryとexceptを付けておく。すると、存在が確認できないフォルダがあってもそこで止まることなく次に進んで、パスが確認できたものだけ移動をする、という流れになります。外付けHDDに移動させるかどうかは、その接続の有無にゆだねているわけです。
早い話が、普通に書くとエラーでプログラムが全て止まるのを、tryとexceptを使うことで限定的なエラーにして、他の正常な処理を止めないようにしたわけです。
こうしておくことで、仮にデータの移動先が外付けHDDであっても、接続していないことでプログラムがエラーになって全て止まることはなくなります。移動がCドライブだけの時でも、外付けだけの時でも、両方の時でも、パスがちゃんと確認されたものはちゃんと移動してくれるようになります。
data_move(移動前フォルダのパス, 移動先フォルダのパス)
tryの中には独自定義関数data_moveの呼び出しを書きます。カッコ内に記入しているのは2つの項目です。第1引数にあたる1つ目の項目には移動前のパスを書いて、第2引数にあたる2つ目の項目には移動先のパスを書きます。
この2つは移動させたい場所によって組み合わせが変わるので共通化はできません。自分でどこにあるデータをどこに移動するかを考えて手動で書き換えたり増やしたりしていきます。
フォルダのデータを取得→存在を確認→移動する、という処理自体はどこからどこへ行くにしても変わりません。違うのはそれぞれの移動元と移動先なので、共通している処理をまとめておいて、それをここで呼び出して使い回しているわけです。
print('【】の取得に失敗')
try内の記述でエラーが発生した時に表示されます。tryでは独自定義関数の呼び出しが書かれていますが、移動前フォルダのパスに不備があった場合は、「【○○】の取得に失敗」という文言が表示されます。【】の中に移動前のフォルダ名を書いておけば、どのフォルダのパスに不備があったかが一目瞭然になります。
input('''Enterキーを押してください''')
これは【プログラムの使い方】の時にも説明した通り、行動を促す働きをします。input()で何かしらの入力を促して、カッコの中はその時に表示するテキストを設定しています。詳細は【使い方】の部分を見てください。