「コマンドプロンプトでバッチを実行すると Python スクリプトも DB 更新も問題なく動くのに、Windows タスクスケジューラに登録すると「成功」と表示されるだけで何も起きていない……」。この記事では、このよくあるハマりポイントを、SYSTEM アカウントでの実行という具体的なケースをもとに、原因の整理と実務で使える解決策・チェックリストまでまとめて解説します。
事例:SYSTEM アカウント実行だと Python の処理結果が出ない
まずは、今回の典型的なケースを整理します。
- OS:Windows(サーバー/クライアントいずれも想定)
- 自動化したい処理:バッチファイル(.bat)から Python スクリプト(.py)を起動し、DB に反映
- 手動実行:
- 管理者権限のコマンドプロンプトでバッチを実行すると、Python も DB 更新も正常に動作
- タスクスケジューラ:
- 「操作」でバッチを指定し、実行アカウントを SYSTEM にして登録
- タスク履歴上は「結果:成功(0x0)」と表示される
- しかし、DB に反映されるはずのデータが反映されない
- Python 側のログも出ていないように見える
タスクスケジューラ側は正常終了扱いなのに、実際の処理は行われていない——このギャップが問題の本質です。
結論から:最短で効いた解決策
このケースでは、次の 2 点を修正することで、Python スクリプトが正常に実行され、DB 反映も確認できました。
- 実行アカウントの変更
- SYSTEM アカウントではなく、手動実行で動いていた自分のユーザー(または権限を付与したサービスアカウント)でタスクを実行する
- バッチ内のパス指定をすべて「絶対パス+ダブルクォート」に統一
python.exeも.pyも、フルパスで指定- スペースを含むパス(
Program Filesなど)を必ず"..."で囲む
たったこれだけで、「手動では動くがタスクスケジューラでは動かない」という状態から脱出できるケースは非常に多いです。とはいえ、再発防止のためにはもう少し深く原因を理解しておくのが安心です。
なぜ手動だと動くのにタスクスケジューラだと動かないのか
ポイントは次の 3 つです。
- 実行アカウントが違う(ユーザー vs SYSTEM)
- 環境変数・カレントディレクトリが違う(ユーザーのシェル vs サービス)
- タスクスケジューラの「成功」は Python の成功とは限らない
まず、タスクスケジューラで SYSTEM アカウント実行にすると、Python スクリプトは「ログオン中のユーザー」ではなく「マシンアカウント(HOSTNAME$)」として動きます。DB やファイルサーバーはこのアカウントに対して権限チェックを行うため、手動実行時に使われていたユーザー権限とは別物として扱われます。
さらに、タスクスケジューラは特に指定しない限り、作業ディレクトリを C:\Windows\System32 として起動します。バッチや Python 内で相対パスを使っていると、.\config.ini や .\.env、相対パスのログ出力などがすべて別の場所を見に行ってしまい、「設定ファイルが見つからない」「ログが出ない」といった事態になります。
そして重要なのは、タスクスケジューラが表示する「成功(0x0)」は、あくまで「指定したプログラムがエラーコード 0 で終了した」という意味でしかない、ということです。Python スクリプト内部で例外を握りつぶしていたり、そもそも python.exe が起動されていなかったりしても、バッチが 0 で終了すれば「成功」扱いになってしまいます。
タスクの「操作」設定はここまで厳密に書く
タスクスケジューラの「操作」は、曖昧に書かず、次のように構造を分けて設定するのがおすすめです。
バッチを使わずに直接 Python を呼び出す例
| 項目 | 設定例 | ポイント |
|---|---|---|
| プログラム / スクリプト | C:\Python311\python.exe | python.exe を絶対パスで指定。PATH に頼らない。 |
| 引数の追加 | "C:\Jobs\job.py" | .py も絶対パスで指定し、パス全体を "..." で囲む。 |
| 開始(次のフォルダーで開始) | C:\Jobs | スクリプトの配置フォルダを指定。相対パス対策の要。 |
バッチ経由で Python を呼び出す例
| 項目 | 設定例 | ポイント |
|---|---|---|
| プログラム / スクリプト | C:\Windows\System32\cmd.exe | コマンドプロンプトを直接呼び出す。 |
| 引数の追加 | /c ""C:\Jobs\run_job.bat"" | /c の後ろのパスを二重引用符で囲むのがコツ。 |
| 開始(次のフォルダーで開始) | C:\Jobs | バッチファイルを置いたフォルダ。ここを忘れると相対パスでコケる。 |
「開始(次のフォルダーで開始)」は見落とされがちですが、ここを正しく設定するだけで「手動で動くのにタスクでは動かない」問題のかなりの割合が解消します。
バッチファイル内の書き方:絶対パス+ダブルクォートが必須
バッチファイル側では、次のように変数を使って絶対パスを管理し、常にダブルクォートで囲む書き方に統一するのがおすすめです。
@echo off
setlocal
set "PY=C:\Python311\python.exe"
set "SCRIPT=C:\Jobs\job.py"
set "WORK=C:\Jobs"
set "LOG=C:\Logs\job.log"
if not exist "C:\Logs" mkdir "C:\Logs"
pushd "%WORK%"
"%PY%" "%SCRIPT%" >> "%LOG%" 2>&1
set "RET=%ERRORLEVEL%"
popd
exit /b %RET%
ポイントは以下の通りです。
set "VAR=..."形式で書くことで、後ろに余計なスペースが入らないpushd/popdで作業ディレクトリを明示的に移動・復元>> "%LOG%" 2>&1で標準出力・標準エラーをまとめてログへリダイレクト- 最後に
exit /b %RET%として、Python の終了コードをタスクにも伝える
こうしておくと、Python 側で例外が発生したときに終了コードが 0 以外になり、タスクスケジューラ上でも「失敗」として検知しやすくなります。
SYSTEM アカウントで失敗しやすい理由
今回のように SYSTEM アカウント 実行にこだわると、次のような落とし穴にはまりがちです。
1. DB や共有フォルダの権限が足りない
SYSTEM で実行すると、Windows 統合認証やファイルアクセスは、ユーザーではなく マシンアカウント(HOSTNAME$) として評価されます。
- SQL Server の Windows 認証で接続している
- ファイルサーバー上の CSV / Excel を読み書きしている
- ネットワークドライブ(Z: など)にアクセスしている
といった処理は、手動実行時は「ログオンユーザー」に対する権限で OK でも、SYSTEM 実行に切り替えた途端、「別人として扱われる」ために権限不足エラーになります。ところが、このエラーがコンソールにしか出ていない、あるいは Python 内で握りつぶしていると、タスクスケジューラからは見えず「成功」になってしまいます。
2. ユーザーのプロファイル(PATH・ユーザー DSN・資格情報)が使えない
SYSTEM アカウントは、通常のユーザープロファイルを持ちません。具体的には次のような違いが出ます。
- PATH:ユーザー環境変数で追加した Python やツールのパスは見えない
- ODBC ユーザー DSN:SYSTEM からは参照不可。OS 全体共通のシステム DSNのみ使用可能
- 資格情報マネージャー:ユーザーとして保存した資格情報が使えない
その結果、手動時は「環境変数のおかげでなんとなく動いていた」コードが、SYSTEM 実行になった途端にモジュールやドライバーを見つけられなくなります。これを避けるためにも、PATH に依存せず絶対パスで書くことが重要になります。
3. 作業フォルダが C:\Windows\System32 になる
タスクスケジューラで「開始(次のフォルダーで開始)」を空欄のままにすると、多くの場合、作業ディレクトリは C:\Windows\System32 になります。Python スクリプト内で次のようなコードを書いていると、ほぼ確実に問題になります。
open("config.yaml")のような相対パスで設定ファイルを開く./logディレクトリを前提にログ出力している.venvや.envをカレントディレクトリから探している
このようなコードは、手動実行時には「スクリプトのあるフォルダ」がカレントディレクトリなので正常に動きますが、タスクスケジューラでは「System32」がベースになってしまうため、ファイルが見つからなくなります。
実行アカウント選びの実務的な指針
以上を踏まえると、Python スクリプトをタスクスケジューラで動かす際の実行アカウントは、次のように考えるのが現実的です。
| パターン | メリット | デメリット / 注意点 |
|---|---|---|
| ログオンユーザー(自分のアカウント) | 手動実行とほぼ同じ環境で動く トラブルシュートが容易 | アカウント変更やパスワード期限の影響を受ける 組織ルールによっては運用上 NG の場合も |
| 専用のサービスアカウント | 権限を最小限に絞れる バッチ・ジョブ用として管理しやすい | DB や共有への権限設定が必要 アカウントの運用(ロック、期限切れ)管理が必要 |
| SYSTEM アカウント | ローカルマシン上の権限は強い ユーザーに依存しない | ネットワーク資源・DB 権限の調整が難しい 環境が特殊でトラブルシュートも難易度高 |
「とにかく早く動かしたい」「まずは原因を切り分けたい」のであれば、手動実行で動いていた自分のユーザー(または同等権限のサービスアカウント)でタスクを動かすところから始めるのが最短ルートです。
Python 仮想環境・バージョン違いによるハマりポイント
タスクスケジューラ経由になることで、Python の実行環境に関する問題が表面化することもよくあります。
仮想環境(venv)を使っている場合
よくあるNGパターンがこちらです。
call C:\Jobs\venv\Scripts\activate.bat
python job.py
これは「手動のコマンドプロンプト」では動きがちですが、タスクスケジューラでは失敗しやすい書き方です。仮想環境を使うときは、activate を呼ばずに直接 python.exe を指定する方が確実です。
set "PY=C:\Jobs\venv\Scripts\python.exe"
"%PY%" "C:\Jobs\job.py"
こうすることで、環境変数 PATH に依存せず、タスクスケジューラから見ても確実に仮想環境の Python を起動できます。
32bit / 64bit の違い
特に ODBC 経由で DB に接続している場合、次の組み合わせミスマッチでハマることがあります。
- 32bit ODBC ドライバー + 32bit Python
- 64bit ODBC ドライバー + 64bit Python
手動実行時には 32bit Python を起動していたのに、タスクスケジューラでは別バージョン(64bit 側)を指してしまう、といったケースです。この場合も、どの python.exe を使うかを絶対パスで明示することで回避できます。
DB・ネットワーク資源にアクセスできないときの確認ポイント
Python スクリプトが本当に動いているかどうかを見極めるために、DB やネットワークアクセス周りのポイントも整理しておきましょう。
- ネットワークドライブ(Z: など)は使わず、UNC パス(
\\server\share)で書く - DB 接続文字列を見直す
- Windows 認証の場合:実行アカウントに DB 権限が付与されているか
- SQL 認証の場合:接続文字列の ID/PW が正しいか(ログ出力で確認)
- ODBC DSN の種類
- ユーザー DSN しか作っていない → SYSTEM アカウントや別ユーザーからは見えない
- OS 全体で共通のシステム DSNを作成するのが安全
タスクスケジューラで動かしたときに DB のログ(監査ログや接続ログ)に記録が残っていない場合は、そもそも接続に到達していないか、権限不足で弾かれている可能性が高いです。
ログ出力で「どこまで動いているか」を見える化する
バッチやタスクスケジューラだけを眺めていても原因が見えにくいので、ログを出して「どこまで処理が進んだか」を可視化するのがトラブルシュートの近道です。
バッチファイルでのログ出力例
@echo off
setlocal
set "PY=C:\Python311\python.exe"
set "SCRIPT=C:\Jobs\job.py"
set "WORK=C:\Jobs"
set "LOG=C:\Logs\job.log"
if not exist "C:\Logs" mkdir "C:\Logs"
echo [%date% %time%] === Task start === >> "%LOG%"
pushd "%WORK%"
echo [%date% %time%] Run python >> "%LOG%"
"%PY%" "%SCRIPT%" >> "%LOG%" 2>&1
echo [%date% %time%] Python exit code: %ERRORLEVEL% >> "%LOG%"
popd
echo [%date% %time%] === Task end === >> "%LOG%"
endlocal
この程度のログでも、次のようなことがすぐに分かります。
- タスクがそもそも起動しているか(ログ自体が出ているか)
- Python 起動前後でエラーになっていないか
- Python の終了コードが 0(成功)か、それ以外か
タスクスケジューラ側のログも有効にする
あわせて、タスクスケジューラの履歴とイベントログも確認しておきましょう。
- タスクの「履歴」を有効化する
- イベントビューアーで
- アプリケーションとサービス ログ > Microsoft > Windows > TaskScheduler > Operational
ここまで見ることで、「タスク自体は起動しているのか」「バッチの起動に失敗していないか」「終了コードはいくつか」といった情報が揃い、Python 側の問題なのか、タスク設定の問題なのかが切り分けやすくなります。
タスクスケジューラの基本設定も見直しておく
地味ですが、次の設定をチェックしておくと運用時のトラブルを減らせます。
- 「ユーザーがログオンしているかどうかにかかわらず実行する」を選択
- バックグラウンドで動かす場合の基本設定
- 「最上位の特権で実行する」にチェック
- 管理者権限での実行が必要な処理(ローカル管理、サービス操作など)がある場合
- 「構成:」は実 OS バージョンに合わせる
- 例:Windows Server 2019 / Windows 10 / Windows 11 など
- トリガーのタイムゾーンや繰り返し設定も確認
- サマータイムのある環境、時刻変更の影響なども意識しておくと安心
これらは直接「Python が動かない」原因ではないかもしれませんが、長期運用時の思わぬトラブルを避けるための大事なチェックポイントです。
再発防止のチェックリスト
最後に、Python+バッチ+タスクスケジューラの組み合わせでトラブルを避けるためのチェックリストをまとめます。新しいタスクを作るときにも、この表をざっとなぞるだけで事故率をかなり減らせます。
| チェック項目 | 確認内容 |
|---|---|
| 実行アカウント | 手動で動いたユーザー、または権限を付与したサービスアカウントで動かしているか。 |
| python.exe のパス | C:\Python311\python.exe など、絶対パス+ダブルクォートで指定しているか。 |
| .py のパス | "C:\Jobs\job.py" のように絶対パスで指定し、パス全体を "..." で囲んでいるか。 |
| 開始フォルダ | 「開始(次のフォルダーで開始)」にスクリプトのフォルダ(例:C:\Jobs)を設定しているか。 |
| 相対パス | Python 内のファイルアクセスが相対パスに依存し過ぎていないか。必要なら __file__ ベースで絶対パス化しているか。 |
| 仮想環境 | venv\Scripts\python.exe を直接呼び出しているか。activate に頼っていないか。 |
| 32bit / 64bit | 使っている Python と ODBC ドライバーのビット数が一致しているか。 |
| ネットワークパス | ネットワークドライブではなく \\server\share 形式の UNC パスを使っているか。 |
| DB 権限 | 実行アカウントに必要な DB 権限が付与されているか(ログイン・SELECT・INSERT など)。 |
| DSN の種類 | ユーザー DSN ではなくシステム DSN を使っているか、あるいは DSN を使わない接続文字列にしているか。 |
| ログ出力 | バッチで >> log 2>&1 を指定し、Python の標準出力・標準エラーがファイルに残るようになっているか。 |
| タスク履歴 | タスクの履歴・イベントログを有効化し、開始・終了・エラー情報を確認できるようにしているか。 |
まとめ:アカウントとパスを疑うのが第一歩
「手動でバッチを叩けば Python も DB 更新も動くのに、タスクスケジューラにすると結果だけ残らない」という問題の多くは、
- 実行アカウントの違い(SYSTEM vs ユーザー)
- パス指定・作業ディレクトリの不整合
この 2 点に集約されます。
今回のケースでは、
- SYSTEM 実行をやめて、手動実行で動作確認済みのユーザーアカウントに変更
- バッチ内の
python.exeと.pyをすべて絶対パス+ダブルクォートで指定
というシンプルな修正だけで、タスクスケジューラ経由でも問題なく Python スクリプトが動作し、DB 反映も確認できるようになりました。
まずはこの 2 点から手を付け、それでも解決しない場合には、本記事で紹介したログ出力・権限・環境差分の観点から順に切り分けていくのがおすすめです。落とし穴さえ押さえておけば、「タスクスケジューラ+バッチ+Python」は Windows 環境での自動処理にとても強力な武器になります。

コメント