Bash error: Text file busy (ETXTBSY)

"Text file busy" means another process holds a write handle on the file you are executing. Close it, or rewrite the file via temp file plus rename.

Error String

bash: ./script.sh: Text file busy

Tldr

The kernel protects running binaries by refusing exec when the file is open for writing. The fix is to finish writing (close the editor or the redirection) before invoking the script, or to write atomically via temp file + `mv`.

Cause

Common in CI pipelines that download a script with `curl > script.sh` and execute it from a subshell that still holds the redirection FD. Also happens when an editor keeps the file open with a write lock.

Repro

# In one terminal:
exec 3> script.sh
# In another:
chmod +x script.sh
./script.sh                   # Text file busy

Fix

# Atomic write pattern:
curl -fsSL https://example.com/script.sh > script.sh.tmp
mv script.sh.tmp script.sh
chmod +x script.sh
./script.sh

Explanation

Atomic rename via `mv` is the canonical fix: the original file is replaced by a new inode, and any held FDs point at the old inode (which is fine to keep writing). The new file is immediately executable.

Faq

Q

Why does mv work when cp fails?

A

`mv` within one filesystem is a rename of the directory entry; it never opens the busy file for writing.

Q

Is deleting the file first safe?

A

Yes on Unix — unlink removes the name while running processes keep the inode until they exit.

Deep Dive

Heading

The kernel protects a running executable

Body

ETXTBSY means you tried to open for writing a file that is currently being executed. Copying a new build over a binary while an instance still runs, or rewriting a script that a running shell is still reading line by line, both hit it. The safe pattern is atomic replacement: write the new content to a temporary file in the same directory, then `mv` it into place. Rename swaps the directory entry, so running processes keep the old inode and finish normally while new invocations pick up the new file.

Heading

Finding what holds the file

Body

`fuser -v ./program` or `lsof ./program` lists the processes using it; on Linux you can also grep `/proc/*/exe`. In containers and CI the culprit is often a background process from an earlier step, or an NFS mount where a stale handle keeps the file busy after the process exits. Note that editing a shell script in place while it runs is dangerous even when it succeeds: Bash reads the file incrementally, so the running script can execute a mix of old and new lines.

Checklist

Write to a temp file and `mv` it into place rather than overwriting.

Use `fuser` or `lsof` to find the process holding the file.

Stop background jobs from earlier build steps before replacing binaries.

Never edit a shell script in place while it is executing.