Troubleshooting

bad interpreter: No such file or directory — when the file is right there

Windows line endings, a wrong shebang or a missing ELF loader make Linux say 'No such file or directory' for a file that exists. Find which one and fix it.

Published ~8 min read

Quick fix: the missing file is not your script but the interpreter named on its first line, the shebang. Most often the script has Windows (CRLF) line endings, so the system looks for /bin/bash followed by an invisible carriage return. Run sed -i 's/\r$//' script.sh (or dos2unix script.sh), then stop your editor or Git from putting the line endings back.

The confusing part is the wording, and the wording depends on your shell. bash 5.1 and 5.3 say /bin/bash^M: bad interpreter: No such file or directory. bash 5.2, the version in Debian 12, Debian 13 and Ubuntu 24.04, says cannot execute: required file not found instead, and the sh on Debian and Ubuntu only prints ./script.sh: not found. All of them mean the same thing, and all of them have the same three causes.

Why does Linux say “No such file or directory” when the file exists?

When you run ./script.sh, the kernel reads the first line, for example #!/bin/bash, and starts that program with your script as its argument. If the program on that line does not exist, starting it fails with “No such file or directory”, and the shell reports that against the file you typed. The missing file is never your script. It is the one its first line points to.

Compiled programs work the same way. A dynamically linked binary names its own loader (for example /lib64/ld-linux-x86-64.so.2), and if that loader is missing you get the same error for a binary that is plainly there. Two commands show you which case you have:

diagnose
head -1 script.sh | od -c     # the shebang, byte by byte: look for \r before \n
file script.sh                # "with CRLF line terminators", or the ELF interpreter for a binary
  • \r before \n, or “CRLF line terminators” — Windows line endings. Cause 1.
  • A clean shebang naming a program you do not have — cause 2.
  • An ELF binary whose interpreter is missing or whose architecture is wrong — cause 3.

Which message does my shell print?

The same Windows-edited script (first line #!/bin/bash plus a carriage return), run as ./crlf.sh, gets a different message on each system:

same script, different messages
shell                  system                          message                                                             exit
bash 5.1               Ubuntu 22.04, Rocky / Alma 9    ./crlf.sh: /bin/bash^M: bad interpreter: No such file or directory  126
bash 5.2               Debian 12 and 13, Ubuntu 24.04  ./crlf.sh: cannot execute: required file not found                  127
bash 5.3               Fedora 44, Alpine 3.24          ./crlf.sh: /bin/bash^M: bad interpreter: No such file or directory  126
dash (/bin/sh)         Debian, Ubuntu                  ./crlf.sh: not found                                                127
BusyBox ash (/bin/sh)  Alpine                          ./crlf.sh: not found                                                127
zsh 5.9                Ubuntu 24.04                    ./crlf.sh: bad interpreter: /bin/bash^M: no such file or directory  127
#!/usr/bin/env bash    any (GNU coreutils)             /usr/bin/env: 'bash\r': No such file or directory                   127

Two practical consequences. Searching your logs for the familiar “bad interpreter” text finds nothing on Debian 12, Debian 13 or Ubuntu 24.04, because bash 5.2 does not print it. And the exit code changes: the same broken script exits 126 on Rocky and 127 on Debian, which matters if a CI job or a monitoring check tests for one of them. What those codes mean in general is in exit code 127.

Cause 1: how do I remove Windows line endings (the ^M)?

Windows ends each line of a text file with two characters, carriage return and line feed (\r\n). Linux uses only the line feed. A script saved on Windows, pasted through some web editors, or checked out by Git on Windows therefore has a \r at the end of every line, and bash prints that character as ^M. The kernel takes the first line literally, so it looks for a program called /bin/bash\r, which does not exist.

Detect it

any Linux
file script.sh                 # ... with CRLF line terminators
head -1 script.sh | od -c      # #   !   /   b   i   n   /   b   a   s   h  \r  \n
grep -c $'\r' script.sh        # number of lines that carry a carriage return

file is not installed on every minimal image; od and grep are.

Fix it

strip the carriage returns
# GNU sed (Debian, Ubuntu, Rocky, Alma, Fedora) and BusyBox sed (Alpine)
sed -i 's/\r$//' script.sh

# every shell script below the current directory
find . -name '*.sh' -exec sed -i 's/\r$//' {} +

# portable version, also fine on macOS
tr -d '\r' < script.sh > script.tmp && mv script.tmp script.sh && chmod +x script.sh

dos2unix does the same and is easier to remember. It is its own package everywhere:

dos2unix
sudo apt install -y dos2unix      # Debian, Ubuntu
sudo dnf install -y dos2unix      # Rocky, Alma, RHEL, Fedora
apk add dos2unix                  # Alpine (BusyBox also has a dos2unix applet)
brew install dos2unix             # macOS

dos2unix script.sh

Stop it coming back

Fixing the file on the server lasts until the next deploy. The fix that sticks lives in the repository: a .gitattributes file tells Git to check shell scripts out with LF endings on every machine, Windows included.

.gitattributes
*.sh        text eol=lf
*.bash      text eol=lf
Dockerfile  text eol=lf
apply it to an existing repository
git add .gitattributes
git add --renormalize .          # re-stages files that were committed with CRLF
git commit -m "Force LF line endings for shell scripts"

# in an existing Windows checkout, after committing or stashing your work:
# delete the affected scripts, then restore them from the repository
git checkout -- path/to/script.sh
  • core.autocrlf is the per-machine setting behind most of these files: with core.autocrlf=true, which the Git for Windows installer suggests by default, Git converts LF to CRLF on checkout. .gitattributes overrides it for the paths it names, and unlike a local setting it travels with the repository.
  • Your editor shows the current line ending in the status bar. In VS Code, click CRLF and choose LF, or set "files.eol": "\n"; in Notepad++ use Edit → EOL Conversion → Unix (LF).

Why does “bash script.sh” run when “./script.sh” fails?

With bash script.sh you start the interpreter yourself, so the shebang is never looked at and its stray \r does no harm. The rest of the file still has carriage returns, though, and they now break individual lines instead:

the same CRLF file, run with bash script.sh
script.sh: line 2: $'\r': command not found
script.sh: line 3: cd: $'/tmp\r': No such file or directory
ls: cannot access '/nonexistent'$'\r': No such file or directory

$'\r': command not found on an empty line is the giveaway. Strip the carriage returns as above rather than relying on calling bash directly.

Cause 2: what if the shebang names a program that is not installed?

If od -c shows a clean first line, read it and check that the program exists:

does the interpreter exist?
head -1 script.sh             # e.g. #!/usr/bin/python
ls -l /usr/bin/python         # the exact path from the shebang
for c in python3 bash node; do command -v "$c"; done   # what this system does have
  • #!/usr/bin/python — Debian 12 and 13, Ubuntu 22.04 and 24.04, and Rocky and Alma 9 name the interpreter python3 and ship no /usr/bin/python. Change the line to #!/usr/bin/env python3, or add the alias: python-is-python3 on Debian and Ubuntu, python-unversioned-command on Rocky and Alma. More in python: command not found.
  • #!/bin/bash on Alpine — Alpine and most images built on it have no bash, only BusyBox sh, which reports ./script.sh: not found. Either apk add bash, or change the shebang to #!/bin/sh if the script avoids bash-only syntax.
  • #!/usr/bin/env node — env searches the PATH, so this fails with /usr/bin/env: 'node': No such file or directory when Node is not installed, or is installed through nvm for another user, or under cron or sudo with a shorter PATH. See npx: command not found for a system-wide install.
  • A moved virtualenv — scripts in venv/bin carry the absolute path of the venv’s Python in their shebang. Rename or copy the venv and every one of them points at a path that no longer exists. Recreate it in its new place instead of moving it: python3 -m venv /srv/app/venv && /srv/app/venv/bin/pip install -r requirements.txt.
  • Options after env — #!/usr/bin/env bash -e makes env look for a program literally called bash -e. Current GNU env says so: use -[v]S to pass options in shebang lines. Write #!/usr/bin/env -S bash -e, or put set -e on the second line.

Cause 3: what if the binary was built for a different system?

For a compiled program, file prints the loader it expects:

what does the binary need?
file ./program
# ELF 64-bit LSB pie executable, x86-64, ..., interpreter /lib64/ld-linux-x86-64.so.2
uname -m          # x86_64 or aarch64: the architecture of this machine
  • A glibc binary on Alpine. Alpine uses musl, so the glibc loader the binary names does not exist and BusyBox reports not found. Use a build made for musl or a static build, base the image on debian:*-slim instead, or try apk add gcompat, a compatibility layer that is enough for simple programs.
  • A 32-bit binary on a 64-bit system. It wants /lib/ld-linux.so.2. Add 32-bit support: sudo dpkg --add-architecture i386 && sudo apt update && sudo apt install -y libc6:i386 on Debian and Ubuntu, sudo dnf install -y glibc.i686 on Rocky and Alma. The next message is usually about a missing library; that one is covered in error while loading shared libraries.
  • The wrong CPU architecture gives a different message: cannot execute binary file: Exec format error, exit 126. An ARM64 build on an x86-64 server, or the reverse. Download the build that matches uname -m.

Why does Docker say “exec /entrypoint.sh: no such file or directory”?

Containers produce the same failure with their own wording. Depending on the Docker version, the log shows exec /entrypoint.sh: no such file or directory or the older standard_init_linux.go:228: exec user process caused: no such file or directory (the line number varies). The file is in the image; one of the three causes above is too. In practice it is usually an entrypoint script checked out on Windows with CRLF endings, or a #!/bin/bash script in an Alpine-based image.

look inside the image
docker run --rm --entrypoint sh myimage -c 'head -1 /entrypoint.sh | od -c'

Fix the file in the repository and add the .gitattributes rule above, so every future build is clean. If you cannot control where the image is built, strip the carriage returns during the build as a safety net:

Dockerfile
COPY entrypoint.sh /entrypoint.sh
RUN sed -i 's/\r$//' /entrypoint.sh && chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
  • bash: ./script.sh: Permission denied, exit 126, means the file lacks its execute bit or sits on a noexec mount. See bash: Permission denied.
  • script.sh: command not found without ./ means the shell searched your PATH, which does not include the current directory. Run it as ./script.sh.

Deploy scripts written on one machine and run on another are where all three causes show up. Running them once on a clean Debian, Ubuntu or Rocky install catches them before production does; on a Standard KVM VPS you can reinstall a different distribution from the panel and test against each.

Frequently asked

What does ^M mean in 'bad interpreter'?

^M is how the terminal shows a carriage return, the extra character Windows puts at the end of every line. '/bin/bash^M: bad interpreter' means the system looked for a program literally named '/bin/bash' plus a carriage return. Strip them with "sed -i 's/\r$//' script.sh" or dos2unix.

How do I convert CRLF to LF on Linux?

Run "sed -i 's/\r$//' file" or install dos2unix and run 'dos2unix file'. For a Git repository, add '*.sh text eol=lf' to .gitattributes and run 'git add --renormalize .' so the scripts stay LF on every machine, Windows included.

Why does bash 5.2 say 'cannot execute: required file not found'?

It is how bash 5.2, the version in Debian 12 and 13 and Ubuntu 24.04, reports a missing interpreter or ELF loader, with exit code 127. bash 5.1 and 5.3 print 'bad interpreter: No such file or directory' and exit 126 instead. The causes are the same: CRLF line endings, a shebang naming a program that is not installed, or a binary built for another system.

Why does 'bash script.sh' run but './script.sh' fail?

Calling bash yourself skips the shebang, so a broken first line no longer matters. If the cause is CRLF line endings, the rest of the file still breaks line by line with errors such as "$'\r': command not found". Convert the file to LF instead of relying on calling bash directly.

Related articles

All troubleshooting