backup.sh: line 3: $'\r': command not found means the script was saved with
Windows line endings and you ran it as bash backup.sh. Every line ends in an
invisible carriage return, and bash treats it as part of the command. Strip the carriage
returns with sed -i 's/\r$//' backup.sh and run it again.
Watch for the quiet damage too: in the test the script kept going after the error, and a
cd on the next line failed, so the rest of the script ran in the wrong
folder.
The error
docker exec fix10-lc-r1 bash -c 'cd /root; bash backup.sh 2>&1'
Backup started
backup.sh: line 3: $'\r': command not found
backup.sh: line 4: cd: $'/tmp\r': No such file or directory
Done in /root
Why it happens
Windows editors can save text with CRLF line endings: a carriage return
(\r) before every newline. Linux bash ends a line at the newline only, so the
carriage return stays in the line. cat -A makes it visible as
^M:
docker exec fix10-lc-r1 bash -c 'cd /root; sed -i ''s/$/\r/'' backup.sh; cat -A backup.sh 2>&1'
#!/bin/bash^M$
echo "Backup started"^M$
^M$
cd /tmp^M$
echo "Done in $(pwd)"^M$
Line 3 is empty in the editor, but to bash it holds one character, \r, which
it tries to run as a command; $'\r' is how bash prints that name. Line 4
asks cd for a folder called /tmp plus a carriage return, which
does not exist. The echo lines still ran because a trailing carriage return
in an argument does not stop echo, and the last line shows the script never
left /root.
Running the same CRLF file as ./backup.sh failed earlier, on the first
line, with a different message; that case has
its own post:
docker exec fix10-lc-r6 bash -c 'cd /root; sed -i ''s/$/\r/'' backup.sh; chmod +x backup.sh; cat -A backup.sh | head -1; ./backup.sh 2>&1; echo exit=$?'
#!/bin/bash^M$
bash: line 1: ./backup.sh: cannot execute: required file not found
exit=127
The fix
docker exec fix10-lc-r1 bash -c 'cd /root; sed -i ''s/\r$//'' backup.sh; cat -A backup.sh 2>&1'
#!/bin/bash$
echo "Backup started"$
$
cd /tmp$
echo "Done in $(pwd)"$
docker exec fix10-lc-r1 bash -c 'cd /root; bash backup.sh 2>&1'
Backup started
Done in /tmp
The sed command deletes a carriage return at the end of each line in place.
After it, cat -A shows only $ at the line ends, the error is
gone and the script finishes in /tmp as intended. My advice, not part of the
test: also save the original file with LF endings in your editor, so the next copy does
not bring the carriage returns back.
How it was reproduced
The capture was made in an ubuntu:24.04 container (Ubuntu 24.04.5 LTS, bash
5.2.21) on Docker Engine 29.9.3. A five-line backup.sh was copied in with
docker cp, converted to CRLF inside the container with
sed -i 's/$/\r/', checked with cat -A, run with
bash backup.sh, converted back and run again. The fix10-lc-r1
container name is the test's own.
Every command was sent from Windows PowerShell 5.1 exactly as shown, with the -c text in single quotes so it reached the container unchanged; a single quote inside that text is written twice, as PowerShell requires.
Frequently asked
- What does $'\r': command not found mean?
- The script has Windows CRLF line endings. Bash read the carriage return on an empty line as a command name and printed it as $'\r'.
- How do I convert a script from CRLF to LF in Linux?
- Run sed -i 's/\r$//' script.sh. In the test cat -A then showed only $ at the line ends and the script ran without errors.
- Why did my script run in the wrong directory after the error?
- A cd line with a carriage return asked for a folder name ending in \r, which failed, and the script carried on. In the test it finished in /root instead of /tmp.
More decoded errors in the Fixes category.