Quick fix
# One-liner: add the execute bit, then run directly
chmod +x setup.sh && ./setup.sh
What you will run into
A script copied from Windows or extracted from an archive fails with Permission denied when run as ./setup.sh, but bash setup.sh runs fine.
You think the file is corrupt or the system refuses to execute it — it's just that the script lacks the execute bit (x); Linux's permission model differs from Windows.
Why this happens
Linux uses the "execute bit" to decide whether a file can be run directly:
./script.shtakes the "direct execution" path; the kernel requires thexbit;bash script.shpasses the file as an argument for bash to read, noxbit needed — so the same file behaves differently depending on how you invoke it.
New files default to 644 (no execute bit); copying from Windows (FAT/exFAT/network drives) or extracting zips often loses the x bit; misconfigured core.fileMode in git repos can also drop it.
Linux runs a script via two paths: ./script.sh is "direct execution" — the kernel checks the x bit, then reads the shebang to find the interpreter;
bash script.sh is "interpreter reads the file" — bash just takes the file as an argument and does not check the execute bit.
That is why the same file behaves differently depending on how you call it.
The execute bit is file metadata (mode bits); new files default to 644 (no x); copying from Windows (FAT/exFAT/network drives do not support POSIX permissions) or extracting from zip (zip does not store the execute bit) commonly drops it;
git losing x on checkout is core.fileMode misconfiguration.
Deterministic takeaway: Permission denied + file exists = missing x — chmod +x fixes it; on WSL mount drives (/mnt/c) chmod is ineffective (drvfs does not support it) → copy into the WSL filesystem then chmod; after zip extraction, apply chmod +x uniformly;
in git the x bit is versioned — git update-index --chmod=+x <file> or fix core.fileMode; fixed diagnostic: ls -l shows the mode — no x in the first column confirms it.
Step by step
1. Confirm it's a permission issue — Required
ls -l setup.sh
Permission bits -rw-r--r-- (no x) → that's the problem (measured: after chmod 644, ./nox.sh reported Permission denied, exit=126).
2. Add the execute bit — Required
chmod +x setup.sh
3. Run again — Required
./setup.sh
Measured: after chmod +x normal output, exit=0.
4. Batch / prevention — Required
chmod +x scripts/*.sh
- Before git commit, confirm
git ls-files -sshows100755(executable); - After unzipping, add x as needed;
- For Windows-mounted drives, handle
core.fileModeas needed (keeping it true is usually more stable).
How to confirm it works
./setup.sh
No more Permission denied and normal execution means success (measured: bash nox.sh also runs, exit=0 — when you don't want to change permissions, invoking via an explicit interpreter works).
Other environments
| Environment | Notes |
|---|---|
| WSL Ubuntu | Same as native Linux; add chmod +x |
| Files copied from Windows | FAT/exFAT/network drives don't carry the execute bit; always check after copying in |
| zip extraction | unzip often drops the x bit; add as needed |
| git repos | core.fileMode=false lets x-bit changes go unsynced; keep it true |
| One-off | bash script.sh / python3 app.py explicit interpreter invocation needs no x bit |
Symptom
$ ./setup.sh
bash: ./setup.sh: Permission denied
ls -lshows-rw-r--r--(no x).bash setup.shworks,./setup.shfails.- Common after copying from Windows, unzipping archives, or creating new files.
Cause
- Linux uses the execute bit; new files default to 644 (no x).
./script.shis "direct execution" (needs x);bash script.shjust reads the file (no x needed).- Windows copies (FAT/exFAT/network), zip extraction and misconfigured git
core.fileModeoften drop the bit.
Solution
chmod +x setup.shthen./setup.sh.- Or run via interpreter:
bash setup.sh/python3 app.py. - Batch: `chmod +x site docs
- Prevention: keep
git ls-files -smode100755; restore x after unzip; keepcore.fileModetrue.
Verification Evidence
- Local (WSL Ubuntu, 2026-08-29):
- Repro 1: chmod 644 +
./nox.sh→Permission denied, exit=126 — matches user error. - Repro 2:
bash nox.sh→ runs, exit=0. - Fix: chmod +x → runs, exit=0.
Evidence details kept locally (2026-08-29)
Applicable tools (1)
This problem is linked from the tool pages below — reachable from either side.
bashBundled solutions (combos)
This problem is part of the following verified bundles.
Source & evidence
Source: https://stackoverflow.com/questions/18960689/ubuntu-says-bash-program-permission-denied
← Coding Agents & Terminal domain | Problem Catalog Experience | Facing another issue? Go to the Helpdesk