DEV Community

Cover image for 'Bad Interpreter: No Such File or Directory' — The Script Exists, the Interpreter Doesn't
SystemCraftDev
SystemCraftDev

Posted on Originally published at systemcraftpress.com

'Bad Interpreter: No Such File or Directory' — The Script Exists, the Interpreter Doesn't

The file is right there, so why does the shell say it doesn't exist? The error is about the interpreter named on the first line, and usually a hidden carriage return is the culprit. Here's how to see it and fix it.

Adapted from the Command Line Essentials Companion Guide.


You write a small script, make it executable, and run it:

$ ls -l deploy.sh
-rwxr-xr-x 1 you you 62 Oct  4 09:12 deploy.sh

$ ./deploy.sh
bash: ./deploy.sh: /bin/bash^M: bad interpreter: No such file or directory
Enter fullscreen mode Exit fullscreen mode

The file is sitting right there, ls just showed it, and the permissions are fine. Yet the shell insists something doesn't exist. This is one of the more disorienting errors in the shell, because the message sounds like it's about your script and it isn't. It's about a different file entirely: the interpreter named on the script's first line. And if you look closely at the message, there's a clue sitting in the path: /bin/bash^M.

What's actually happening

When you run ./deploy.sh, the shell doesn't execute the text file itself. The operating system looks at the first line, the shebang (#!/bin/bash), takes the path after #! as the program that should run the script, and launches that program with your script as its argument. If that interpreter path doesn't point to a real file, the launch fails with "No such file or directory". The "file" it can't find is the interpreter, not your script.

So why would /bin/bash not exist? Because the path isn't /bin/bash. Look at the ^M at the end. That's how terminals display a carriage return character (\r), the extra invisible character Windows puts before every newline. A file saved with Windows-style (CRLF) line endings has its first line stored as #!/bin/bash\r\n. Linux and macOS treat only \n as the line break, so the \r stays attached to the end of the path. The system goes looking for a program literally named bash followed by a carriage return, finds nothing, and reports the mismatch.

Nothing is wrong with your commands. The file just arrived with the wrong kind of line endings, usually because it was written in a Windows editor, copied from a Windows machine, or checked out of Git on Windows with automatic line-ending conversion turned on.

The fix, step by step

1. Confirm it's line endings. The file command reports them directly:

$ file deploy.sh
deploy.sh: Bourne-Again shell script, ASCII text executable, with CRLF line terminators
Enter fullscreen mode Exit fullscreen mode

"With CRLF line terminators" is the diagnosis. If you want to see the characters themselves, cat -A deploy.sh shows each carriage return as ^M and each line end as $.

2. Strip the carriage returns. Any of these works:

dos2unix deploy.sh
Enter fullscreen mode Exit fullscreen mode
sed -i 's/\r$//' deploy.sh
Enter fullscreen mode Exit fullscreen mode

dos2unix is the cleanest if it's installed (sudo apt install dos2unix on Debian and Ubuntu). The sed command needs nothing extra: it deletes a trailing \r from every line, in place.

3. Run it again. Nothing else about the script needs to change, and the executable bit survives the edit:

$ ./deploy.sh
Enter fullscreen mode Exit fullscreen mode

4. Stop it from coming back. Fixing the file once doesn't help if your editor or Git reintroduces the problem next time. In VS Code, click CRLF in the bottom-right status bar and switch it to LF for shell scripts. If the file lives in a Git repository, a .gitattributes line makes the setting stick for everyone who clones it:

*.sh text eol=lf
Enter fullscreen mode Exit fullscreen mode

Two mistakes worth knowing about ahead of time

Retyping the shebang line and hoping for the best. Deleting and retyping #!/bin/bash in the same editor will often produce the same CRLF ending again, because the editor is what's adding it. Fix the setting, not just the line.

Assuming ^M errors only affect the first line. Every line in the file has the carriage return, which is why scripts without a shebang fail in a different, stranger way: running them with bash deploy.sh can produce errors like $'\r': command not found partway through, because each line's trailing \r is treated as part of the command. Different message, same root cause. If you see \r or ^M anywhere in shell error output, check line endings first.

The same error message also appears when the interpreter path is genuinely wrong, for example #!/usr/bin/python on a system that only has python3. If file reports no CRLF terminators, check that the path on the first line points at something that exists (ls /usr/bin/python), or use #!/usr/bin/env python3 so the system finds it for you.

A habit that prevents the confusion entirely

When "No such file or directory" appears for a file you can plainly see, ask which file the error is actually about. If you're running a script, the answer might be the interpreter on the first line rather than the script itself. Run file on the script and read the first line with head -1 deploy.sh | cat -A. Two quick commands will tell you whether you have a wrong path or an invisible character, and either way the error stops being mysterious.


If you'd like more posts like this sent straight to your inbox, subscribe to the newsletter.

Prefer to dig in yourself? The Command Line Essentials repo on GitHub has more free examples and exercises.

Top comments (0)