はじめに (対象読者・この記事でわかること)
この記事は、Pythonのsubprocessモジュールを使ってディレクトリサイズを取得し、その出力結果を保持したいと考えている中級者以上のプログラマーを対象にしています。特に、システム管理ツールやバックアップスクリプトの作成に取り組んでいる方に最適です。
本記事を読むことで、subprocess.check_callとcheck_outputの違いを理解し、ディレクトリサイズを取得する方法を習得できます。さらに、出力結果を保持するための様々なテクニック(check_outputの利用、一時ファイルの使用、パイプの活用など)を具体的なコード例と共に学ぶことができます。これにより、より堅牢で実用的なPythonスクリプトを開発するスキルを向上させることができるでしょう。
前提知識
この記事を読み進める上で、以下の知識があるとスムーズです。 前提となる知識1 (例: Pythonの基本的な知識とプログラミング経験) 前提となる知識2 (例: コマンドラインインターフェースの基本的な操作) 前提となる知識3 (例: Linux/Unix環境での基本的なコマンドの知識)
subprocessモジュールとディレクトリサイズ取得の概要
Pythonのsubprocessモジュールは、外部コマンドを実行するための強力なツールです。特にシステム管理や自動化スクリプト作成において不可欠な存在です。このモジュールには、コマンドを実行するための様々な関数が用意されており、その中でもcheck_callとcheck_outputはよく使われます。
check_callはコマンドが正常終了した場合にNoneを返し、エラーが発生した場合は例外を発生させます。一方、check_outputはコマンドの出力結果を文字列として返します。この違いが、ディレクトリサイズを取得する際に重要になります。
ディレクトリサイズを取得するには、Linux/Unix環境ではduコマンドが一般的に使用されます。例えば、du -sh /path/to/directoryと実行すると、指定したディレクトリのサイズが人間が読みやすい形式で表示されます。
しかし、check_callはコマンドの出力を保持しないため、取得したサイズを後で利用することができません。この問題を解決するための様々な方法を本記事では紹介します。
具体的なディレクトリサイズ取得方法と出力保持テクニック
ステップ1:check_callの基本的な使い方と限界
まずは、check_callを使ってディレクトリサイズを取得する基本的な方法から見ていきましょう。以下に簡単な例を示します。
Pythonimport subprocess # ディレクトリサイズを取得するコマンドを実行 try: subprocess.check_call(['du', '-sh', '/path/to/directory']) except subprocess.CalledProcessError as e: print(f"コマンド実行に失敗しました: {e}")
このコードを実行すると、指定したディレクトリのサイズがコンソールに出力されます。しかし、この方法ではサイズ情報を変数に保持することができません。check_callはコマンドが正常に実行されたかどうかを確認するための関数であり、出力結果を返すことを意図していないからです。
ステップ2:check_outputを使った出力の保持
出力結果を保持する最も直接的な方法は、check_outputを使用することです。check_outputはコマンドの出力を文字列として返すため、これを変数に格納して後から利用できます。
Pythonimport subprocess # ディレクトリサイズを取得し、出力を保持 try: output = subprocess.check_output(['du', '-sh', '/path/to/directory'], universal_newlines=True) print(f"取得したサイズ情報: {output}") # 出力から数値部分を抽出(例: "1.2G" のような形式) size_str = output.split('\t')[0] print(f"サイズ: {size_str}") except subprocess.CalledProcessError as e: print(f"コマンド実行に失敗しました: {e}")
この方法では、universal_newlines=Trueを指定することで、出力を文字列として扱うことができます。これにより、出力結果を変数に格納し、必要に応じて加工して利用できます。
ステップ3:一時ファイルを使った方法
場合によっては、コマンドの出力を一時ファイルに保存し、後からそのファイルを読み込む方法も有効です。特に、大量の出力を扱う場合や、後から参照する必要がある場合に便利です。
Pythonimport subprocess import tempfile import os # 一時ファイルを作成 with tempfile.NamedTemporaryFile(mode='w+', delete=False) as temp_file: temp_path = temp_file.name try: # コマンドを実行し、出力を一時ファイルにリダイレクト subprocess.check_call(['du', '-sh', '/path/to/directory'], stdout=temp_file) # 一時ファイルから読み込み with open(temp_path, 'r') as f: content = f.read() print(f"取得したサイズ情報: {content}") # 出力から数値部分を抽出 size_str = content.split('\t')[0] print(f"サイズ: {size_str}") finally: # 一時ファイルを削除 os.unlink(temp_path)
この方法では、一時ファイルを使用することで、コマンドの出力を永続的に保持できます。ただし、一時ファイルの作成と削除の手間がかかるというデメリットがあります。
ステップ4:パイプを使った方法
さらに高度な方法として、パイプを使って複数のコマンドを組み合わせる方法があります。例えば、duコマンドの出力を別のコマンドに渡して加工する場合に有効です。
Pythonimport subprocess # パイプを使って複数のコマンドを組み合わせる try: # duの出力をgrepでフィルタリングし、その結果を保持 process = subprocess.Popen( ['du', '-sh', '/path/to/directory'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True ) output, error = process.communicate() if process.returncode == 0: print(f"取得したサイズ情報: {output}") # 出力から数値部分を抽出 size_str = output.split('\t')[0] print(f"サイズ: {size_str}") else: print(f"エラー: {error}") except Exception as e: print(f"例外が発生しました: {e}")
この方法では、subprocess.Popenを使用してプロセスを直接制御し、stdoutとstderrをパイプで受け取ります。これにより、より柔軟な出力処理が可能になります。
ハマった点やエラー解決
標準エラー出力の扱い
ディレクトリサイズ取得の際、duコマンドはエラーが発生した場合に標準エラー出力にメッセージを出力します。このエラーメッセージを適切に処理しないと、問題の特定が困難になります。
Pythonimport subprocess try: # stderrをPIPEにリダイレクト output = subprocess.check_output( ['du', '-sh', '/nonexistent/directory'], stderr=subprocess.PIPE, universal_newlines=True ) except subprocess.CalledProcessError as e: # エラー時の標準エラー出力を表示 print(f"エラー: {e.stderr.strip()}")
エンコーディングの問題
特にPython 3では、文字列の扱いが厳格になっているため、エンコーディングの問題に注意が必要です。universal_newlines=Trueを指定することで、テキストモードで出力を扱うことができます。
Pythonimport subprocess # universal_newlines=Trueを指定して文字列として扱う try: output = subprocess.check_output( ['du', '-sh', '/path/to/directory'], universal_newlines=True ) print(output) except subprocess.CalledProcessError as e: print(f"コマンド実行に失敗しました: {e}")
パーミッションの問題
ディレクトリサイズを取得する際、対象ディレクトリへの読み取り権限がない場合にエラーが発生します。このような場合には、エラーハンドリングを適切に行う必要があります。
Pythonimport subprocess import os def get_directory_size(directory_path): try: # ディレクトリの存在と読み取り権限を確認 if not os.path.exists(directory_path): raise FileNotFoundError(f"ディレクトリが存在しません: {directory_path}") if not os.access(directory_path, os.R_OK): raise PermissionError(f"ディレクトリを読み取る権限がありません: {directory_path}") output = subprocess.check_output( ['du', '-sh', directory_path], universal_newlines=True ) return output.split('\t')[0] except (FileNotFoundError, PermissionError) as e: print(f"エラー: {e}") return None except subprocess.CalledProcessError as e: print(f"コマンド実行に失敗しました: {e}") return None # 使用例 size = get_directory_size('/path/to/directory') if size: print(f"ディレクトリサイズ: {size}")
まとめ
本記事では、subprocessモジュールを使ってディレクトリサイズを取得し、出力結果を保持する方法について解説しました。
- check_callは出力結果を保持しないため、ディレクトリサイズを取得するには不向きです
- check_outputを使うことで、出力結果を文字列として保持し、後から利用できます
- 一時ファイルやパイプを活用する方法もあり、状況に応じて適切な手法を選択できます
- エラーハンドリングやエンコーディング、パーミッション問題など、実際の開発で注意すべき点も紹介しました
この記事を通して、Pythonでシステムコマンドを実行し、その結果を効果的に利用するスキルを身につけることができたはずです。これにより、より高度なシステム管理ツールや自動化スクリプトの開発が可能になります。
今後は、マルチスレッド環境でのsubprocessの利用や、より複雑なコマンドの組み合わせについても記事にする予定です。
参考資料
参考にした記事、ドキュメント、書籍などがあれば、必ず記載しましょう。
- Python公式ドキュメント - subprocessモジュール
- duコマンドのマニュアルページ
- Pythonで外部コマンドを実行する方法 - Qiita
- Pythonでファイルサイズやディレクトリサイズを取得する方法 - note.nkmk.me
