python3 -m venv venv のあと、./venv/bin/activate と打つと zsh: permission denied で止まる。sh venv/bin/activate にすると今度は何も表示されず、有効化もされていない。どちらも原因は同じで、activate は「実行するもの」ではなく「いまのシェルに読み込ませるもの」だからです。
先に答えを書きます。
source venv/bin/activate
# または
. venv/bin/activate
そのあと echo $VIRTUAL_ENV と which python3 を打ち、どちらも作った venv の中を指していれば有効化できています。プロンプトの (venv) 表示だけで判断しないほうがいい理由は、後半の「移動した venv」の節で実際の出力を見せます。
以下の出力は、このサイトの運営に使っているMac(macOS 26.6.2・zsh 5.9)で、2026年10月19日に /tmp の下へ一時フォルダを作って再現したものです。ホームフォルダのパスは ~ に置き換えています。出力に出てくる /private/tmp/itmemo34.… は、再現のために作った一時フォルダの名前です(作業を2回に分けたので2種類あります)。
3通りの打ち方を並べて試した
同じ venv に対して、よく見かける3つの打ち方を順に試しました。シェルの設定ファイルを読まない素の zsh(zsh -f)を端末として開き、そこで打った記録です。
% ./venv/bin/activate
zsh: permission denied: ./venv/bin/activate
% echo $?
126
% sh venv/bin/activate
% echo $? "[$VIRTUAL_ENV]"
0 []
% source venv/bin/activate
(venv) % echo $? "[$VIRTUAL_ENV]"
0 [/private/tmp/itmemo34.bpGSmT/venv]
(venv) % which python3
/private/tmp/itmemo34.bpGSmT/venv/bin/python3
| 打ち方 | 表示 | 終了コード | 有効化 |
|---|---|---|---|
./venv/bin/activate | zsh: permission denied: ./venv/bin/activate | 126 | されない |
sh venv/bin/activate | 何も出ない | 0 | されない |
zsh venv/bin/activate | 何も出ない | 0 | されない |
source venv/bin/activate | プロンプトに (venv) | 0 | される |
. venv/bin/activate | プロンプトに (venv) | 0 | される |
source と . は zsh でも bash 3.2(macOS 付属)でも同じ結果でした。
permission denied の読み方
zsh: permission denied: ./venv/bin/activate は、行の先頭が zsh: なのでシェルが出したメッセージです。Python はまだ起動していません。終了コード 126 は「ファイルは見つかったが実行できなかった」という意味で、ファイルが無いときの 127 とは区別されます。venv を作っていないフォルダで同じコマンドを打つと、こうなります。
% ./venv/bin/activate
zsh: no such file or directory: ./venv/bin/activate
% echo $?
127
こちらが出たら、activate の問題ではなく場所の問題です。pwd で今いるフォルダを確かめ、ls venv/bin で activate があるかを見ます。
実際に権限を見ると、activate には実行権限(x)が付いていません。pip や python には付いています。
% python3 -m venv venv
% ls -l venv/bin
total 72
-rw-rw-r-- 1 me wheel 1909 10月 19 00:14 activate
-rw-rw-r-- 1 me wheel 858 10月 19 00:14 activate.csh
-rw-rw-r-- 1 me wheel 1998 10月 19 00:14 activate.fish
-rw-rw-r-- 1 me wheel 8834 10月 19 00:14 Activate.ps1
-rwxr-xr-x 1 me wheel 251 10月 19 00:14 pip
-rwxr-xr-x 1 me wheel 251 10月 19 00:14 pip3
-rwxr-xr-x 1 me wheel 251 10月 19 00:14 pip3.9
lrwxr-xr-x 1 me wheel 7 10月 19 00:14 python -> python3
lrwxr-xr-x 1 me wheel 38 10月 19 00:14 python3 -> ~/opt/anaconda3/bin/python3
lrwxr-xr-x 1 me wheel 7 10月 19 00:14 python3.9 -> python3
左端の10文字が権限で、1文字目が種類(- は普通のファイル、l はリンク)、続く3文字が持ち主の権限です。activate は rw- で x が無く、pip は rwx です。
付け忘れではなく、ファイルの先頭にもそう書いてあります。
% head -2 venv/bin/activate
# This file must be used with "source bin/activate" *from bash*
# you cannot run it directly
なお、同じ操作をシェルスクリプトの中や zsh -c で行うと zsh:1: permission denied: のように行番号が入り、bash では bash: ./venv/bin/activate: Permission denied と大文字で出ます。表記が違っても中身は同じです。
sh で読むと「何も起きない」理由
sh venv/bin/activate は新しいシェル(子プロセス)を起動し、その中で activate を読み込みます。PATH や VIRTUAL_ENV が書き換わるのはその子プロセスの中だけで、読み終わると子プロセスごと消えます。元のシェルには何も残らないので、エラーも出ず終了コードも 0 になります。zsh venv/bin/activate でも同じでした。
source(または .)は子プロセスを作らず、いま開いているシェル自身にファイルを読ませます。activate がやりたいのは「いまのシェルの PATH を書き換える」ことなので、この形でしか目的を果たせません。
chmod +x を付けても直らない
permission denied を見て実行権限を付けたくなりますが、一時フォルダの venv で試すと、エラーが消えるだけで有効化はされませんでした。
% chmod +x venv/bin/activate
% ./venv/bin/activate; echo "rc=$?"
rc=0
% echo "VIRTUAL_ENV=[$VIRTUAL_ENV]"
VIRTUAL_ENV=[]
% which python3
~/opt/anaconda3/bin/python3
% chmod -x venv/bin/activate
実行できるようになっても、結局は子プロセスで読まれて消えるので、sh で読んだときと同じです。試したあとは最後の行の chmod -x で元に戻しています。
which python3 の「有効化前」はMacによって違う
有効化の確認として「有効化前は /usr/bin/python3 を指している」と説明されることが多いのですが、このMacでは違いました。
$ which -a python3
~/opt/anaconda3/bin/python3
/usr/bin/python3
$ python3 --version
Python 3.9.7
Anaconda を入れたときに ~/.zshrc へ conda init の設定が書き込まれていて、PATH の先頭に Anaconda が来ているためです。比較のために、Anaconda を外した PATH で同じコマンドを打つと /usr/bin/python3(Xcode Command Line Tools の 3.9.6)になりました。Homebrew の /opt/homebrew/bin には python3.11 と python3.12 しかなく、番号なしの python3 はありませんでした。
$ env PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin" zsh -f -c "which -a python3; python3 --version"
/usr/bin/python3
Python 3.9.6
つまり「有効化前は何を指すか」は覚えるものではなく、自分のMacで一度 which -a python3 を打って確かめるものです。判定は、有効化したあとに venv の中を指しているかどうかだけで足ります。
もう1つ影響があります。python3 -m venv venv は「その時点の python3」を元に venv を作るので、このMacでは Anaconda の 3.9.7 を元にした venv ができていました。元になった Python は venv/pyvenv.cfg で確認できます。
% cat venv/pyvenv.cfg
home = ~/opt/anaconda3/bin
include-system-site-packages = false
version = 3.9.7
home が元の Python の場所、version がその版です。Homebrew の 3.12 で作りたいなら python3.12 -m venv venv のように番号付きで呼びます。pip3 がどの Python につながっているかの確認はpip3 がどの Python を指しているか確認する方法にまとめています。
有効化したあとは、Python 自身にも聞けます。sys.prefix が venv、sys.base_prefix が元の Python なら有効化できています。
% source venv/bin/activate
(venv) % python3 -c "import sys; print(sys.executable); print(sys.prefix); print(sys.base_prefix)"
/private/tmp/itmemo34b.KwotTA/venv/bin/python3
/private/tmp/itmemo34b.KwotTA/venv
~/opt/anaconda3
/tmp の下に作ったのに /private/tmp と出るのは、macOS の /tmp が /private/tmp へのリンクだからです(ls -ld /tmp で /tmp -> private/tmp と表示されます)。venv はリンクをたどった実際の場所を記録します。
(base) と (venv) が両方出るとき
conda init が入っている端末では、プロンプトの頭に (base) が付いています。設定ファイルを書き換えずに同じ状態を一時的なシェルで作り、venv を有効化すると次のようになりました。
% eval "$(~/opt/anaconda3/bin/conda shell.zsh hook)"
(base) % source venv/bin/activate
(venv) (base) % which python3
/private/tmp/itmemo34.bpGSmT/venv/bin/python3
(venv) (base) % echo "[$CONDA_DEFAULT_ENV] [$VIRTUAL_ENV]"
[base] [/private/tmp/itmemo34.bpGSmT/venv]
(venv) (base) % deactivate
(base) %
(venv) (base) と並んでいても、which python3 は venv を指しているので問題ありません。CONDA_DEFAULT_ENV は base のまま残りますが、PATH の先頭は venv です。deactivate すると (base) の状態に戻ります。
(venv) と出ているのに中身が違う:venv を移動・コピーした場合
プロンプトだけで判断しないほうがいい一番の理由がこれです。一時フォルダの venv を別のフォルダへ移してから有効化しました。
% mkdir renamed
% mv venv renamed/venv
% cd renamed
% source venv/bin/activate
(venv) % echo $VIRTUAL_ENV
/private/tmp/itmemo34b.KwotTA/venv
(venv) % which python3
~/opt/anaconda3/bin/python3
(venv) % which pip
~/opt/anaconda3/bin/pip
(venv) は出ています。しかし VIRTUAL_ENV は移動前の場所のままで、そこにはもう何も無いので、python3 も pip も PATH の次にある Anaconda に落ちています。この状態で pip install すると、エラーも出ずに Anaconda 側へ入ります。venv の中の pip を直接呼ぶと、原因がはっきり出ます。
(venv) % ./venv/bin/pip --version
zsh: ./venv/bin/pip: bad interpreter: /private/tmp/itmemo34b.KwotTA/venv/bin/python3: no such file or directory
(venv) % head -1 venv/bin/pip
#!/private/tmp/itmemo34b.KwotTA/venv/bin/python3
bad interpreter: の後ろが、pip の1行目(shebang)に書かれている Python の場所です。activate も pip も、作ったときの絶対パスを中に書き込んでいるため、フォルダを移したり名前を変えたりすると壊れます。公式ドキュメントも venv を「移動やコピーができるものとはみなさない」と書いています(venv — 仮想環境の作成(Python 公式ドキュメント))。プロジェクトフォルダを移したあとは、後の節の手順で作り直します。コピー(cp -R)でも同じで、コピー先で有効化すると VIRTUAL_ENV はコピー元を指していました。
別の venv へ切り替えるとき
2つの venv を用意しました。app-a は python3(このMacでは Anaconda の 3.9.7)で作って six を入れたもの、app-b は Homebrew の python3.12 で作ったものです。A を有効化したまま、deactivate せずに B の activate を読みました。
% source app-a/venv/bin/activate; echo "A: $VIRTUAL_ENV"
A: /private/tmp/itmemo34b.KwotTA/app-a/venv
(venv) % source app-b/venv/bin/activate; echo "B: $VIRTUAL_ENV"
B: /private/tmp/itmemo34b.KwotTA/app-b/venv
((venv) ) % echo $PATH | tr ":" "\n" | head -3
/private/tmp/itmemo34b.KwotTA/app-b/venv/bin
~/.local/bin
~/opt/anaconda3/bin
((venv) ) % python --version
Python 3.12.13
((venv) ) % python -c "import six"
Traceback (most recent call last):
File "<string>", line 1, in <module>
ModuleNotFoundError: No module named 'six'
((venv) ) % deactivate
%
読み取れることは3つあります。
- PATH に A は残っていません。activate の中で
deactivate nondestructiveが呼ばれ、前の venv を外してから読み込む作りになっているためです(今回作った 3.9 と 3.12 の activate の両方にこの行がありました)。 - ライブラリは venv ごとに別なので、A に入れた
sixは B からは見えずModuleNotFoundErrorになります。切り替えたら import できなくなった、というのはこの状態です。 - プロンプトが
((venv) )と二重のかっこになっています。これは切り替えのせいではなく、このMacの Homebrew 3.12.13 で作った venv では、新しいシェルで普通に有効化しても毎回こう表示されました。activate の中がPS1="("'(venv) '") ${PS1:-}"という書き方になっていて、VIRTUAL_ENV_PROMPTの(venv)にさらにかっこを足しているためです。表示が崩れているだけで、echo $VIRTUAL_ENVは正しい場所を指していました。
インストールしたはずなのに import できないときの切り分けはライブラリは入っているのに import できない理由に書いています。
venv を作り直す手順
移動して壊れた、Python の版を変えたい、何を入れたか分からなくなった、という場合は直すより作り直すほうが早いです。A の venv(Anaconda の 3.9.7 製)を Homebrew の 3.12 で作り直しました。
% source venv/bin/activate
(venv) % python -m pip freeze > requirements.txt
(venv) % cat requirements.txt
six==1.17.0
(venv) % deactivate
% pwd
/tmp/itmemo34b.KwotTA/app-a
% rm -rf venv
% python3.12 -m venv venv
% source venv/bin/activate
((venv) ) % python --version
Python 3.12.13
((venv) ) % python -m pip install -q --disable-pip-version-check -r requirements.txt
((venv) ) % python -m pip list --disable-pip-version-check
Package Version
------- -------
pip 26.1.2
six 1.17.0
((venv) ) % cat venv/pyvenv.cfg
home = /opt/homebrew/opt/python@3.12/bin
include-system-site-packages = false
version = 3.12.13
executable = /opt/homebrew/Cellar/python@3.12/3.12.13_4/Frameworks/Python.framework/Versions/3.12/bin/python3.12
command = /opt/homebrew/opt/python@3.12/bin/python3.12 -m venv /private/tmp/itmemo34b.KwotTA/app-a/venv
requirements.txtは venv の外(プロジェクト直下)に書き出します。venv の中に置くとrm -rf venvで一緒に消えます。rm -rfの前にpwdで場所を確かめます。消すのは venv フォルダだけで、自分で書いたコードは venv の外にあるので残ります。- このMacでは
python3が Anaconda なので、版を変えたいときはpython3.12のように番号付きで呼ばないと、また 3.9.7 の venv ができます。 - 作り直したあとの
pyvenv.cfgはhomeとversionが 3.12 に変わり、3.12 では作ったときのコマンド(command)まで記録されていました。 pip freezeの出力に pip 自身は含まれず、自分で入れたsixだけが書き出されました。
deactivate を先にする理由も確かめました。有効化したまま venv を消すと、VIRTUAL_ENV は消えた場所を指したまま、python3 は黙って Anaconda の 3.9.7 に落ちます。エラーが出ないので、気づかずに作業を続けてしまいます。
% source venv/bin/activate
((venv) ) % rm -rf venv
((venv) ) % echo "VIRTUAL_ENV=[$VIRTUAL_ENV]"
VIRTUAL_ENV=[/private/tmp/itmemo34b.KwotTA/app-b/venv]
((venv) ) % which python3
~/opt/anaconda3/bin/python3
((venv) ) % python3 --version
Python 3.9.7
csh や fish を使っている場合
venv の bin には、シェルごとに別の activate が入っています(上の ls -l の activate.csh、activate.fish)。macOS には csh(tcsh)が最初から入っているので、3.12 で作り直した venv で試しました。
> source venv/bin/activate
Badly placed ()'s.
> source venv/bin/activate.csh
(venv) > echo $VIRTUAL_ENV
/private/tmp/itmemo34b.KwotTA/app-a/venv
(venv) > which python3
/private/tmp/itmemo34b.KwotTA/app-a/venv/bin/python3
(venv) > deactivate
zsh/bash 用の activate を csh で読むと Badly placed ()'s. で止まります。これは csh が bash の関数定義の書き方 deactivate () { を読めないという意味で、activate.csh を読めば有効化できました。fish なら source venv/bin/activate.fish です(fish はこのMacに入っていないので試していません)。シェルごとのコマンドは公式ドキュメントの表にまとまっています。自分のシェルは echo $SHELL で確かめられます。
VS Code の中のターミナルや実行ボタンでは、ここまでとは別に「どの Python を選んでいるか」の設定が効きます。ターミナルでは有効化できるのにエディタでだけ違う場合はVS Code で仮想環境が自動認識されない理由と対処法を見てください。
検証した環境
この記事の調査・再現・執筆は、運営者の指示のもとでAIエージェント(AnthropicのClaude)が行い、公開するかどうかは運営者が決めています。載せた出力は、そのAIエージェントがこのMacで実際にコマンドを実行して得たものです。
- macOS 26.6.2(Apple シリコン)
- zsh 5.9(ログインシェル)、比較に bash 3.2.57(macOS 付属)
~/opt/anaconda3/bin/python3:Python 3.9.7(PATH の先頭、conda init による)/usr/bin/python3:Python 3.9.6(Xcode Command Line Tools)/opt/homebrew/bin/python3.12:Python 3.12.13(Homebrew)- 実施日:2026年10月19日。すべて
/tmpの下に作った一時フォルダで実行し、シェルの設定ファイルは変更していません
コメントを残す