Skip to content
DevOps AI ToolKit
Newsletter
All guides
AI for Linux Admins By James Joyner IV · · 9 min read Last reviewed Jul 2026

Linux Error Guide: './script.sh: No such file or directory' — Fix Shebang and Line Endings

Quick answer

Learn why './script.sh No such file or directory' appears even when the file exists, and how to fix shebangs, CRLF line endings, and broken ELF loaders.

  • #linux
  • #troubleshooting
  • #errors
  • #bash
Free toolkit

Stuck on this Linux Admins error? Get the free incident triage checklist

A one-page PDF — the exact steps to isolate, fix, and verify a production error like this one. No spam, unsubscribe anytime.

Overview

Few Linux errors are as confusing as being told a file does not exist while you are staring right at it in ls. You run a script, and the shell responds:

./script.sh: No such file or directory

The key insight is that this message is often not about script.sh at all. When you execute a file, the kernel reads its first bytes to decide how to run it. For a text script, the kernel looks at the shebang line (#!/path/to/interpreter) and tries to launch that interpreter. For a compiled binary, it consults the ELF header and loads the requested dynamic loader. If the interpreter or loader is missing, malformed, or points at a nonexistent path, the kernel’s execve() call returns ENOENT — “No such file or directory” — and the shell attributes the error to the file you named. The file exists; the thing it asks the kernel to run does not.

This guide walks through the four most common causes and gives you a repeatable way to pin down which one you are hitting.

Symptoms

  • ls -l script.sh clearly shows the file, and cat script.sh prints its contents, yet ./script.sh returns No such file or directory.
  • The file has the execute bit set (chmod +x was already run), ruling out a simple permission problem, which would instead say Permission denied.
  • Running the interpreter explicitly, such as bash script.sh, sometimes works even though ./script.sh fails — a strong hint the shebang is the culprit.
  • For a binary, ./myprogram fails identically, and the file came from a different distribution, container base image, or CPU architecture.
  • The error may appear only after editing the file on Windows or copying it through a Windows share.

Common Root Causes

  1. Wrong or missing shebang interpreter path. The script starts with #!/usr/local/bin/bash but on this host bash lives at /bin/bash or /usr/bin/bash. The kernel tries the literal path in the shebang, cannot find it, and fails.
  2. Interpreter named in the shebang is not installed. A script begins with #!/usr/bin/python3 or #!/usr/bin/env node, but Python 3 or Node is not present on this minimal server or container.
  3. CRLF (Windows) line endings. The file was saved with \r\n endings, so the shebang line is literally #!/bin/bash\r. The kernel looks for an interpreter named /bin/bash\r (bash followed by a carriage return) and cannot find it. This is the single most common cause on mixed Windows/Linux teams.
  4. Wrong-architecture ELF binary or missing dynamic loader. An x86_64 binary run on aarch64 (or vice versa), or a dynamically linked binary whose ELF interpreter — for example /lib64/ld-linux-x86-64.so.2 — is absent, produces the same ENOENT.

Diagnostic Workflow

Start by asking the file what it actually is. file reads the magic bytes and tells you script versus binary, architecture, and often whether CRLF endings are present.

file script.sh
# script.sh: Bourne-Again shell script, ASCII text executable, with CRLF line terminators

If file mentions “CRLF line terminators,” you have found the problem. Confirm it by looking for the trailing carriage return. cat -A renders \r as ^M and line ends as $:

cat -A script.sh | head -5
# #!/bin/bash^M$

You can inspect the raw first bytes directly to see the 0d (carriage return) after the shebang path:

head -c 40 script.sh | xxd
# look for 0d 0a at the end of the first line

Fix CRLF endings with dos2unix, or with sed if that tool is not installed:

dos2unix script.sh
# or, without dos2unix:
sed -i 's/\r$//' script.sh

If the endings are clean, verify the shebang path actually resolves. Read the first line, then confirm the interpreter exists:

head -1 script.sh
# #!/usr/local/bin/bash
which bash
# /usr/bin/bash   <-- mismatch: the shebang points somewhere bash is not
command -v python3

The portable fix is to use env so the interpreter is resolved via PATH rather than hardcoded: change #!/usr/local/bin/bash to #!/usr/bin/env bash.

For binaries, inspect the ELF program headers. readelf -l reveals the requested interpreter (the dynamic loader) and the architecture:

readelf -h myprogram | grep -E 'Class|Machine'
readelf -l myprogram | grep -i interpreter
# [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]

Then confirm every shared library — including that loader — can be resolved:

ldd myprogram
# libssl.so.3 => not found        <-- missing dependency

If readelf shows a Machine that does not match uname -m, you have an architecture mismatch and need the correct build. If the requested loader path does not exist on disk, install the matching C library package (for example glibc or the multi-arch loader).

Example Root Cause Analysis

A developer pushes a deployment script that runs fine on their laptop but fails in the CI runner with ./deploy.sh: No such file or directory. The runner engineer confirms the file is present and executable, so permissions are not the issue.

Running file deploy.sh reports Bourne-Again shell script, ASCII text executable, with CRLF line terminators. That last clause is the smoking gun: the script was authored in a Windows editor and committed without normalization. cat -A deploy.sh | head -1 shows #!/bin/bash^M$, proving the kernel is searching for an interpreter literally named /bin/bash\r.

The immediate fix is sed -i 's/\r$//' deploy.sh, after which ./deploy.sh runs correctly. To prevent recurrence, the team adds a .gitattributes entry (*.sh text eol=lf) so Git always checks out shell scripts with LF endings regardless of the contributor’s platform, and adds a pre-commit hook that rejects files containing \r.

Prevention Best Practices

  • Use #!/usr/bin/env bash (or env python3) instead of hardcoded absolute interpreter paths, so scripts remain portable across distributions where the binary lives in different directories.
  • Add a .gitattributes file with *.sh text eol=lf to force Unix line endings on checkout, and configure editors (or .editorconfig) to save with LF.
  • Run a linter such as shellcheck in CI; it flags many portability issues before they reach a server.
  • For distributed binaries, build against the oldest glibc you must support, or ship statically linked builds to avoid missing-loader failures.
  • Validate architecture explicitly in deployment pipelines with uname -m and file checks so an x86_64 artifact never lands on an arm64 host.

Quick Command Reference

file script.sh                      # script vs binary, arch, CRLF detection
cat -A script.sh | head -5          # show ^M (CR) and $ (line end)
head -c 40 script.sh | xxd          # inspect raw shebang bytes for 0d
head -1 script.sh                   # read the shebang line
dos2unix script.sh                  # strip CRLF endings
sed -i 's/\r$//' script.sh          # strip CRLF without dos2unix
which bash; command -v python3      # confirm interpreter path exists
uname -m                            # host architecture
readelf -h myprogram                # ELF class and machine
readelf -l myprogram | grep -i interp   # requested dynamic loader
ldd myprogram                       # resolve shared libraries and loader

Conclusion

The No such file or directory error on an existing file is the kernel’s way of telling you that the interpreter or loader the file requests cannot be found. Work outward from file, then check for CRLF endings, verify the shebang path, and for binaries inspect the ELF interpreter and its shared libraries. Nine times out of ten the answer is a stray \r or a hardcoded interpreter path — both easy to fix and even easier to prevent with env shebangs and .gitattributes normalization.

Free download · 368-page PDF

Fixed it? Get 500 Linux Admins & DevOps AI prompts — free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.

Did this fix your issue?

Free download · 368-page PDF

Get 500 Battle-Tested DevOps AI Prompts — Free

500 battle-tested, copy-paste AI prompts engineered by a senior systems engineer — every one with fill-in placeholders and safety/back-out notes. Drop your email and it's yours.

  • 500 prompts: Linux · Kubernetes · Terraform · OpenStack · GitLab · Docker · Monitoring · Incident Response
  • Instant PDF download — yours free, forever
  • Plus one practical AI-workflow email a week (no spam)

Single opt-in · unsubscribe anytime · no spam.