ターミナルで ./hello.py と打ったら zsh: permission denied: ./hello.py が出た、という場合、Python スクリプトを動かしたいだけなら次の形で足ります。
python3 hello.py
python3 に「このファイルを読んで実行して」と渡す形なので、スクリプト側に実行権限は要りません。./hello.py の形にこだわる場合だけ、実行権限と1行目の #!(shebang)の両方が必要になります。片方だけだと、permission denied が別のエラーに変わるだけです。
以下は、このサイトの運営に使っているMac(macOS 26.6.2・zsh 5.9)で、2026年10月19日に /tmp の下へ一時フォルダを作って打った記録です。中身が print("hello") の1行だけの hello.py を使っています。ホームフォルダのパスは ~ に置き換えています。
まず再現:何が起きているか
% ls -l hello.py
-rw-r--r-- 1 me wheel 15 10月 19 00:09 hello.py
% ./hello.py
zsh: permission denied: ./hello.py
% echo $?
126
% python3 hello.py
hello
読み方は次のとおりです。
- 行の先頭の
zsh:は、このメッセージを出したのがシェル(zsh)だという印です。Python はまだ起動していないので、コードの中身は関係ありません。 ls -lの左端-rw-r--r--は、1文字目が種類(-は普通のファイル)、次の3文字rw-が持ち主の権限です。x(実行)が無いので、./で「プログラムとして起動」できません。echo $?の 126 は「見つかったが実行できなかった」という終了コードです。
似た表示との区別も試しました。./ を付けずに hello.py とだけ打つと、シェルは PATH の中からコマンドを探すので、今いるフォルダのファイルは見に行きません。
% hello.py
zsh: command not found: hello.py
% echo $?
127
Python が起動したあとで出る PermissionError: [Errno 13] は、スクリプトが開こうとしたファイルの権限の問題で、こちらとは別物です。そちらはPermissionError: [Errno 13] の記事で扱っています。
chmod +x だけだと syntax error に変わる
permission denied を見て実行権限を付けると、今度はこうなりました。
% chmod u+x hello.py
% ./hello.py
./hello.py: line 1: syntax error near unexpected token `"hello"'
./hello.py: line 1: `print("hello")'
Python の文法エラーに見えますが、Python は起動していません。zsh のマニュアル(man zshmisc の COMMAND EXECUTION)に、実行形式でないファイルは「シェルスクリプトとみなして /bin/sh で実行する」と書かれていて、そのとおりの動きです。
$ MANPAGER=cat man zshmisc | col -b | grep -A3 "not in executable format"
If execution fails because the file is not in executable format, and
the file is not a directory, it is assumed to be a shell script.
/bin/sh is spawned to execute it. If the program is a file beginning
with `#!', the remainder of the first line specifies an interpreter for
このMacの /bin/sh の中身は bash でした。zsh で打ったのに line 1: syntax error near unexpected token という bash の書式で出るのはこのためです。
$ ls -l /private/var/select/sh
lrwxr-xr-x 1 root wheel 9 8月 13 11:51 /private/var/select/sh -> /bin/bash
1行目が import sys のファイルだと、出方がまた変わります。import というコマンドを探して見つからず、2行目で文法エラーになります。
$ printf "import sys\nprint(sys.executable)\n" > where.py; chmod +x where.py; ./where.py; echo "rc=$?"
./where.py: line 1: import: command not found
./where.py: line 2: syntax error near unexpected token `sys.executable'
./where.py: line 2: `print(sys.executable)'
rc=2
line 1: や command not found が Python のトレースバック(Traceback (most recent call last):)の形をしていなければ、シェルがファイルを読んでいる、と見分けられます。
1行目に shebang を書くと ./ で動く
1行目に #!/usr/bin/env python3 を足すと、同じ起動方法で Python が使われます。どの Python が使われたかを表示させました。
% cat where.py
#!/usr/bin/env python3
import sys
print(sys.executable)
% ./where.py
~/opt/anaconda3/bin/python3
% source venv/bin/activate
((venv) ) % ./where.py
/private/tmp/itmemo99.GcOUVJ/venv/bin/python3
((venv) ) % deactivate
#!/usr/bin/env python3 は「PATH で最初に見つかる python3 で動かす」という意味です。このMacでは Anaconda が PATH の先頭にいるので、何もしなければ Anaconda の 3.9.7 で動きました。仮想環境を有効化すると、その中の Python に切り替わります。python3 hello.py で動かす場合も、使われるのは同じく PATH で最初の python3 です。((venv) ) という表示は、このMacの Homebrew 3.12 で作った venv の表示のくせで、動作には関係ありません(詳しくはvenv が有効化できないときの記事に書きました)。
shebang の書き方は Python 公式チュートリアルの付録「実行可能な Python スクリプト」にあります(付録(Python 公式ドキュメント))。#! はファイルの最初の2文字でなければならず、改行は Unix 形式である必要がある場合がある、とも書かれています。次の2つはその実例です。
#!/usr/bin/python と書いた場合
% ./old.py
zsh: ./old.py: bad interpreter: /usr/bin/python: no such file or directory
% echo $?
127
bad interpreter: の後ろが、1行目に書かれた Python の場所です。このMac(macOS 26.6.2)には /usr/bin/python が無く、/usr/bin/python3 だけがありました。古い記事のスクリプトをそのまま使うと出る形です。
Windows で作ったファイル(改行が CRLF)の場合
% ./crlf.py
env: python3\r: No such file or directory
% echo $?
127
% perl -pi -e 's/\r$//' crlf.py
% ./crlf.py
1
env: python3\r の \r は、行末の改行コード CR が名前にくっついて読まれているという意味です。env は「python3 と CR」という名前のコマンドを探して見つけられていません。行末の CR を取り除くと動きました。エディタの改行コード設定を LF にしても同じです。
ディレクトリを ./ で実行しても同じ表示になる
打ち間違えてフォルダ名を実行した場合も、zsh では同じ文言でした。bash では is a directory と出るので分かりやすいのですが、zsh では区別がつきません。
% ls -ld tools
drwxr-xr-x 2 me wheel 64 10月 19 00:09 tools
% ./tools
zsh: permission denied: ./tools
$ ./tools; echo "rc=$?" # bash で実行
bash: ./tools: is a directory
rc=126
ls -ld の1文字目が d ならディレクトリです。
フォルダ側に x が無いと python3 でも動かない
ファイルではなく、それを入れているフォルダの x(中に入る権限)が無い場合も試しました。この場合は python3 に渡しても Python 側で同じ理由で失敗します。
% ls -ld locked
drw-r-xr-x 3 me wheel 96 10月 19 00:09 locked
% ./locked/run.py
zsh: permission denied: ./locked/run.py
% python3 locked/run.py
python3: can't open file '/private/tmp/itmemo99.GcOUVJ/locked/run.py': [Errno 13] Permission denied
% chmod u+x locked
% ./locked/run.py
ok
drw-r-xr-x の持ち主の欄が rw- で x がありません。フォルダの x は「中のファイルにたどり着く」ための権限なので、中の run.py に実行権限があっても届きません。最後の2行のとおり、chmod u+x locked でフォルダに x を戻すと動きました。python3 側のメッセージは can't open file の後ろに絶対パス、最後に [Errno 13] が付く形で、こちらは Python が出しています。python3 ファイル名 でも permission denied が出るときは、途中のフォルダを ls -ld で1つずつ見ます。
避けたほうがいい対処
sudo ./hello.py:原因は「起動のしかた」なので、管理者権限は関係ありません。chmod 777 hello.py:自分以外のユーザーにも書き込みを許してしまいます。足りないのは持ち主の x だけなのでchmod u+xで足ります。
Apple のサポートページでも、ファイルを実行可能にする方法として chmod が案内されています(Macの「ターミナル」でファイルを実行可能ファイルにする(Apple サポート))。ただしそこで扱っているのはシェルスクリプトで、Python スクリプトの場合は上のとおり shebang も要ります。
検証した環境
この記事の調査・再現・執筆は、運営者の指示のもとでAIエージェント(AnthropicのClaude)が行い、公開するかどうかは運営者が決めています。載せた出力は、そのAIエージェントがこのMacで実際にコマンドを実行して得たものです。
- macOS 26.6.2(Apple シリコン)
- zsh 5.9(ログインシェル)、比較に bash 3.2.57(macOS 付属、
/bin/shの中身もこれ) ~/opt/anaconda3/bin/python3:Python 3.9.7(PATH の先頭)/usr/bin/python3:Python 3.9.6(Xcode Command Line Tools)。/usr/bin/pythonは無し- 仮想環境は
/opt/homebrew/bin/python3.12(Python 3.12.13)で作成 - 実施日:2026年10月19日。
/tmpの下の一時フォルダで実行
コメントを残す