Shell script fails in WSL? Check CRLF and LF line endings

A shell script edited on Windows can fail in WSL even though the file is present. A bad interpreter message, or an unexpected error from an otherwise familiar command, can come from a carriage return left at the end of a line.

CRLF line endings are common on Windows; LF is the usual convention on Linux. For scripts run with Bash in WSL, checking the line endings and trying an LF copy can help establish whether that difference is responsible.

Inspect the line endings

In a WSL Linux terminal, open the folder containing the script. These examples use hello.sh and require Bash and GNU sed, as available in environments such as Ubuntu. Display the first three lines with otherwise invisible characters made visible:

sed -n '1,3l' hello.sh

A line ending shown as \r$ contains a carriage return before the newline. With LF alone, the line ends with $. The dollar sign here marks the end of the displayed line; it is not something to add to the script. If nothing appears, check the file’s contents and the path you selected.

A CRLF shebang such as #!/bin/bash can make direct execution treat the carriage return as part of the interpreter path. Seeing /bin/bash^M in the error is a reason to inspect that first line as well as the script’s existence.

Compare how the script is started

For a script you own and whose contents you understand, invoking Bash explicitly can behave differently from starting the file directly:

bash hello.sh

This asks the Bash you invoked to read the script, rather than using the shebang to select an interpreter. Carriage returns in the body can still become part of commands or options. An error may be followed by more output, so read the messages along the way instead of judging success only from the last line or exit code.

./hello.sh

Direct execution also needs execute permission and an interpreter at the path in the first line. Messages such as bad interpreter or No such file or directory have several possible causes. If the line endings are already LF, continue with the interpreter path, invocation method, and permissions.

Make an LF copy while keeping the original

If CRLF appears to be involved, write a separate working copy. First make sure hello-lf.sh is an unused filename: an existing file with that name will be overwritten.

sed 's/\r$//' hello.sh > hello-lf.sh

This removes carriage returns at line ends and writes the result to another file. Keep the input and output names different; redirecting into the input file can empty it before it is read. This operation does not convert the overall character encoding or remove a BOM.

bash hello-lf.sh

Compare the LF copy’s result with the original. If it resolves the error, save that script with LF line endings in your usual editor too. Keep conversion limited to the files you have identified rather than changing a whole folder at once.

Keep shell scripts consistent in Git

A project can specify line-ending rules in .gitattributes. Add the following rule alongside existing settings, reviewing any other rules that apply to the same files:

*.sh text eol=lf

This treats matching *.sh files as text and requests LF when Git checks them out. Keeping the rule in the project gives it a different scope from changing a global Git setting.

git check-attr text eol -- hello.sh

For example, the effective attributes for the named script can appear as:

hello.sh: text: set
hello.sh: eol: lf

The applicable rule and the current file’s contents are separate things to check. For a tracked file, compare the index and working tree:

git ls-files --eol -- hello.sh

i/lf describes the index; w/crlf describes the working file. Adding a rule does not immediately rewrite the file you already have open. An i/lf and w/crlf combination therefore means the working copy still has CRLF. Untracked files do not appear in this listing, which is another reason to inspect the file directly with sed.

Normalizing an existing repository can affect many diffs. Review the target script and agree on the project’s line-ending policy before incorporating a wider change.

LF scripts also matter when building a WSL containers image from your own script. After an edit, git diff –check can inspect whitespace problems; a clean result does not establish that the shell script runs correctly.

References