here-document gives 'unexpected end of file' error
Stefan Bogdanescu
Founder & Senior Architect
Decoding the Error: Why Here-Documents Cause 'Unexpected End of File' in Bash
As developers, we often bridge the gap between application logic (like PHP or JavaScript) and the underlying operating system commands. When dealing with shell scripting, especially when managing file input and output streams, subtle syntax errors like "unexpected end of file" can feel like insurmountable roadblocks. Today, we are diving deep into a very common pitfall in Bash: how to correctly implement here-documents for multi-line text generation, particularly when piping data to external commands like /var/mail.
If you’ve encountered the error while trying to format an email body via a script, rest assured, this is a known issue related to delimiter handling. Let's break down what is happening and how to fix it, offering robust solutions that hold up whether you are scripting system tasks or building complex applications on platforms like Laravel.
Understanding the Here-Document Mechanism
A here-document (or heredoc) in Bash is a powerful feature that allows you to treat a block of text as input for a command, much like reading from a file. It is defined by starting with a delimiter (like << EOF) and ending when the shell encounters an exact match for that delimiter on a line by itself.
The structure is designed to be clean:
command << DELIMITER
Content lines here...
DELIMITER
When you see the error warning: here-document at line y delimited by end-of-file (wanted 'EOF'), it means the shell started reading your input block, expected to find the closing delimiter (EOF), but reached the absolute end of the script file without finding it. This usually points to one of three issues: an incorrect delimiter spelling, misplaced lines, or invisible characters/whitespace that confuse the parser.
The Root Cause and Practical Fixes
The error you are seeing stems from how Bash parses the stream between the opening << and the closing EOF. In your specific case with sending mail via /var/mail, the issue is likely related to whitespace, line breaks, or how the shell interprets the command execution context.
Best Practice: Strict Delimiter Placement
The most reliable way to use heredocs is to ensure the opening delimiter (<< EOF) and the closing delimiter (EOF) are on separate lines, with absolutely no leading or trailing spaces around the delimiter itself. The content that follows should be indented relative to the command that initiates the redirection.
Corrected Example:
Instead of mixing the command execution directly into the heredoc structure like this:
/var/mail -s "$SUBJECT" "$EMAIL" < EOF
Line 1
Line 2
EOF
You should treat the heredoc as the input to a command, not part of the command itself. The shell needs to know exactly what block of text belongs to the redirection operator (<).
Here is the robust way to structure your script:
SUBJECT="Test Email"
EMAIL="recipient@example.com"
cat << EOF > /var/mail -s "$SUBJECT" "$EMAIL"
This is the first line of my message!
And here's another line!
This is the last line of the message.
EOF
Why This Works Better
- Clarity: By using
cat << EOF, you are explicitly telling the shell to feed the entire block of text generated between the delimiters into the standard input of thecatcommand, which then redirects that output directly to/var/mail. - Isolation: This isolates the multi-line text generation from the execution command, preventing parsing conflicts with the redirection operator itself.
If you are building complex data structures or processing user input in your scripts—whether deploying infrastructure or managing system tasks—understanding these fine-grained shell mechanics is crucial for reliable automation. This attention to detail mirrors the rigorous structure required when working with frameworks like Laravel, where understanding how components interact at a low level ensures stability across the stack.
Conclusion: Scripting for Stability
The "unexpected end of file" error in heredocs is rarely about the content itself; it is almost always about the syntax surrounding the delimiters. By strictly adhering to the rule of placing the closing delimiter on its own, unindented line, and by using standard input redirection (<) correctly, you eliminate ambiguity for the shell parser.
Mastering these subtle shell details elevates your scripting skills from simple execution to robust system automation. Always test your boundaries—ensure every opening tag has a corresponding, perfectly matched closing tag—and your terminal scripts will run smoothly.