If there's a "First World Problems" for scripting, this would be it.

I have the following code in a script I'm updating:

if [ $diffLines -eq 1 ]; then
        dateLastChanged=$(stat --format '%y' /.bbdata | awk '{print $1" "$2}' | sed 's/\.[0-9]*//g')

        mailx -r "Systems and Operations <sysadmin@[redacted].edu>" -s "Warning Stale BB Data" jadavis6@[redacted].edu <<EOI
        Last Change: $dateLastChanged

        This is an automated warning of stale data for the UNC-G Blackboard Snapshot process.

        echo "$diffLines have changed"

The script sends email without issues, but the mailx command is nested within an if statement so I appear to be left with two choices:

  1. Put EOI on a new line and break indentation patterns or
  2. Keep with indentation but use something like an echo statement to get mailx to suck up my email.

I'm open to alternatives to heredoc, but if there's a way to get around this it's my preferred syntax.

up vote 97 down vote accepted

You can change the here-doc operator to <<-. You can then indent both the here-doc and the delimiter with tabs:

#! /bin/bash
cat <<-EOF
echo Done

Note that you must use tabs, not spaces to indent the here-doc. This means the above example won't work copied (Stack Exchange replaces tabs with spaces). There can not be any quotes around the first EOF delimiter, else parameter expansion, command substitution, and arithmetic expansion are not in effect.

  • Cool, that fixes the indent problem but now it's not expanding the variable I'm trying to put in there ($dateLastChanged) if I do the hypen+quotes thing in your example, but if I take the hyphen and quotes out and put EOI on a new line it starts expanding it again. – Bratchley May 20 '13 at 18:17
  • 1
    @JoelDavis: Just remove the quotes, keep the hyphen. – choroba May 20 '13 at 18:37
  • 4
    Being forced to use tabs is very annoying. Is there a good way around it? – con-f-use Nov 19 '15 at 18:25
  • 2
    @con-f-use: You can try something like cat << EOF | sed 's/^ *//' and so on. – choroba Nov 19 '15 at 19:33
  • 2
    Or even better: cat <<- EOF | awk 'NR==1 && match($0, /^ +/){n=RLENGTH} {print substr($0, n+1)}'. This removes the amount of preceding spaces in the first line from every line in the here document (thanks to anubhava). – con-f-use Nov 21 '15 at 10:40

Try this:

sed 's/^ *//' >> ~/Desktop/text.txt << EOF
    Load time-out reached and nothing to resume.
    $(date +%T) - Transmission-daemon exiting.

If you don't need command substitution and parameter expansion inside your here-document, you can avoid using tabs by adding the leading spaces to the delimiter:

$     cat << '    EOF'
>         indented
>     EOF
$     cat << '    EOF' | sed -r 's/^ {8}//'
>         unindented
>     EOF

I couldn't figure out a way to use this trick and keep parameter expansion, though.

  • To me, this is the only answer which solves the indenting problem without using spaces. shell-check will find any indent changes which con't match the spaces in the quoted string. Use double quotes for parameter expansion? – Tom Hale Jun 26 at 17:18

Hmm... Seems like you could take better advantage of the --format argument here to use --printf instead and just pass the lot over a pipe. Also, your if...fi is a compound command - it can take a redirect which all contained commands will inherit, so maybe you don't need to nest the heredoc at all.

if      [ "$diffLines" = 1 ]
then    stat --printf "Last Change: %.19y\n\n$(cat)\n" /.bbdata |
        mailx   -r  "Systems and Operations <sysadmin@[redacted].edu>" \
                -s  "Warning Stale BB Data" 'jadavis6@[redacted].edu'
else    echo    "$diffLines have changed"
fi      <<\STALE
This is an automated warning of stale data for the UNC-G Blackboard Snapshot process.
  • Yeah my previous revision said I didn't mind the sed/awk part. Part of my revision today was to take it out since it wasn't germane to the question. Either way it's six of one half a dozen of the other. – Bratchley Jan 27 '15 at 16:27
  • @Bratchley - damn. That last sentence is going to distract me for the rest of the day. – mikeserv Jan 27 '15 at 16:28
  • How do you mean? – Bratchley Jan 27 '15 at 16:29
  • 1
    @Bratchley - Looks like a riddle. – mikeserv Jan 27 '15 at 16:30
  • Ha. Not sure what country you're from but that's a common phrase in the States. Just means "Different approach to the same end." Your solution does get around heredoc though. – Bratchley Jan 27 '15 at 16:31

The other method would be herestrings:

    mail_content="Last Change: $dateLastChanged

    This is an automated warning of stale data for the UNC-G Blackboard Snapshot process."
    mailx -r "Systems and Operations <sysadmin@[redacted].edu>" -s "Warning Stale BB Data" jadavis6@[redacted].edu <<<"$mail_content"

Your Answer


By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.