Skip to content
ohsudo, Home
macOS

"zsh: permission denied" when running a script: the execute bit, and other causes

Fix "zsh: permission denied" on macOS or Linux: add the execute bit with chmod +x, check the path is a file and not a folder, and spot noexec mounts and missing rights.

Beginner3 min readApplies to macOS 10.15+ · zsh · Linux

by OhSudo ·

% ./deploy.sh
zsh: permission denied: ./deploy.sh

zsh, the default shell on macOS, found the file and wasn’t allowed to run it. Almost always, the file isn’t marked as executable. Files you download, copy from email, or create in an editor start out as plain text, and they need permission to run.

TL;DR

Terminal window
chmod +x deploy.sh
./deploy.sh

Or run it through its interpreter, which needs no execute bit:

Terminal window
bash deploy.sh

1. Look at the permissions

Terminal window
ls -l deploy.sh
-rw-r--r-- 1 alex staff 412 Nov 12 10:00 deploy.sh

There’s no x in -rw-r--r--, so nobody may execute it. Add it for everyone, or just for you:

Terminal window
chmod +x deploy.sh
chmod u+x deploy.sh
-rwxr-xr-x 1 alex staff 412 Nov 12 10:00 deploy.sh

The chmod Calculator shows what 755, u+x and the others mean, in both directions.

In Git, the execute bit is recorded. If a script loses it after each clone, store it in the repository: git update-index --chmod=+x deploy.sh, then commit.

2. The path is a folder

zsh gives the same message when you try to run a folder:

% /usr/local/bin
zsh: permission denied: /usr/local/bin

Folders do have an x permission, but for a folder it means “may enter”, not “may run”. Check what the path is with ls -ld, and add the program’s name: /usr/local/bin/mytool. To go into the folder, use cd.

A typo in a variable often causes this one. $TOOLS_DIR/mytool with an empty TOOLS_DIR turns into /mytool, and a variable holding just a folder name runs the folder.

3. It’s executable, and still refused

You can’t enter a folder on the way. Every folder in the path needs x for you. ls -ld on each one shows it, and namei -l /path/to/deploy.sh lists them all at once, on Linux.

A disk mounted with noexec. On Linux, /tmp, removable drives and some network shares are often mounted so that nothing on them can run. Check with:

Terminal window
findmnt -T deploy.sh -o TARGET,OPTIONS

Run it with bash deploy.sh, or copy it somewhere else. On macOS, mount shows a similar noexec flag for some network and external volumes.

macOS protections. A script in Desktop, Documents or Downloads, or on an external disk, may need your terminal to have access. That usually gives Operation not permitted rather than this, as explained in Operation not permitted.

4. A different message, after the fix

Once it’s allowed to run, another problem may show up:

zsh: ./deploy.sh: bad interpreter: /bin/bash^M: no such file or directory

The ^M means Windows line endings: see the bad interpreter section of the bash article.

zsh: command not found: deploy.sh

You typed it without ./, so zsh searched your PATH, which doesn’t include the current folder. Use ./deploy.sh.

Warning

Don’t use chmod 777, or sudo, to make this go away. 777 lets any user change the script, and anything you then run as root. Only the execute bit is missing, so chmod +x is enough. Also read a script you downloaded before you make it executable.

Shell, we have a problem!

Similar, but different: bash: ./deploy.sh: Permission denied is the same problem in bash: see Permission denied when running a script. Operation not permitted is a macOS protection, even with sudo. command not found: brew is about PATH. And is damaged and can't be opened is Gatekeeper. zsh: bad CPU type in executable is a program built for another processor.

Found this useful? Share it.

Found a mistake? Let us know at contact@ohsudo.com.

Tools

Try it yourself

Articles

Related articles