Emacs编辑含here document的Shell脚本出现unexpected end of file错误的解决方法
I’ve run into this exact issue before—Shell here documents are super picky about their closing markers! The root problem is that the closing marker (like EOF) must be completely left-aligned, with no leading spaces or indentation whatsoever, and Emacs’ default auto-indent behavior tends to add spaces to that line, which breaks things. Here are a few solid ways to keep using Emacs for your shell scripts without hitting this error:
1. Manually Ensure the Closing Marker is Left-Aligned
The simplest quick fix is to just make sure your here document’s closing tag sits flush against the left margin, no matter how indented the surrounding code is. For example:
if [ -f /etc/os-release ]; then cat <<EOF === System Information === OS: $(grep PRETTY_NAME /etc/os-release | cut -d= -f2) Hostname: $(hostname) EOF fi
Even though the cat command is indented under the if block, the final EOF has to be completely unindented. Emacs might try to auto-indent it as you type—just hit backspace until it’s all the way left.
2. Enable Emacs’ Shell Here Doc Indent Mode
Emacs’ built-in sh-mode has a dedicated mode to handle here documents correctly. This will let you keep auto-indenting your code, while automatically ensuring the closing marker stays left-aligned:
- Open your shell script (Emacs should auto-enter
sh-modefor.shfiles; if not, runM-x sh-mode). - Enable the mode manually with
M-x sh-here-doc-indent-mode. - To make this permanent for all shell scripts, add this to your
~/.emacsor~/.emacs.d/init.elconfiguration file:(add-hook 'sh-mode-hook 'sh-here-doc-indent-mode)
Once enabled, Emacs will indent the content inside your here document nicely, but will never add leading spaces to the closing marker.
3. Use <<- for Tab-Indented Closing Markers
If you prefer to keep your closing marker indented (to match the rest of your code), you can use the <<- syntax instead of <<—this allows the closing marker to have leading tab characters (not spaces). To make this work with Emacs:
- Modify your here document to use
<<-:if [ -f /etc/os-release ]; then cat <<-EOF === System Information === OS: $(grep PRETTY_NAME /etc/os-release | cut -d= -f2) Hostname: $(hostname) EOF fi - Set Emacs to use tabs instead of spaces for indentation in
sh-mode. Either runM-x set-variableand setindent-tabs-modetotfor the current buffer, or add this to your config to make it default:(add-hook 'sh-mode-hook (lambda () (setq indent-tabs-mode t)))
The - in <<- tells the shell to ignore any leading tabs before the closing marker, so your indentation stays clean and the script runs without errors.
Why Nano Works but Emacs Doesn’t
Nano doesn’t auto-indent code by default, so you probably didn’t add any leading spaces to the closing marker when editing there. Emacs’ auto-indent feature, while great for most code, doesn’t know about Shell’s strict here document rules out of the box—hence the "unexpected end of file" error when it adds spaces to that closing line.
内容的提问来源于stack exchange,提问作者computronium

