Can't indent heredoc to match code block's indentation

50,398

Solution 1

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
    indented
    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.

Solution 2

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

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

Solution 3

Try this:

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

Solution 4

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"

Solution 5

You could use a here-string (<<<) instead of a here-document (<<MARKER), which at least avoids the end-of-document marker not being intended.

if [[ true ]]; then
  # Sample indented block
  cat <<<'First line
  Second line
  '
fi

Output (note the trailing empty line):

First line
  Second line
  

You can combine this with other commands to strip indent. Here cut will output the fifth character onwards of each line. -c is character mode, and 5- is the range of characters to output.

if [[ true ]]; then
  # Sample indented block
  cut -c5- <<<'
    First line
    Second line
  '
fi

Output (note first and last line are empty):


First line
Second line

Excellent explanation of the difference at command line - What's the difference between <<, <<< and < < in bash? - Ask Ubuntu.

Share:
50,398
Bratchley
Author by

Bratchley

Updated on September 18, 2022

Comments

  • Bratchley
    Bratchley over 1 year

    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.
    EOI
    
    else
            echo "$diffLines have changed"
    fi
    

    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.

    • Christopher King
      Christopher King over 2 years
      Very First World!
    • G-Man Says 'Reinstate Monica'
      G-Man Says 'Reinstate Monica' about 2 years
      It’s very rare that you need to combine awk and sed in the same command.  Here. you could do stat … | awk '{sub(/\.[0-9]*/, ""); print $1, $2}', thus doing the substitution in awk and eliminating the sed.  (Use gsub if there is a possibility of multiple occurrences of the pattern.)
  • Bratchley
    Bratchley almost 11 years
    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.
  • choroba
    choroba almost 11 years
    @JoelDavis: Just remove the quotes, keep the hyphen.
  • Bratchley
    Bratchley over 9 years
    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.
  • mikeserv
    mikeserv over 9 years
    @Bratchley - damn. That last sentence is going to distract me for the rest of the day.
  • Bratchley
    Bratchley over 9 years
    How do you mean?
  • mikeserv
    mikeserv over 9 years
    @Bratchley - Looks like a riddle.
  • Bratchley
    Bratchley over 9 years
    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.
  • con-f-use
    con-f-use over 8 years
    Being forced to use tabs is very annoying. Is there a good way around it?
  • choroba
    choroba over 8 years
    @con-f-use: You can try something like cat << EOF | sed 's/^ *//' and so on.
  • con-f-use
    con-f-use over 8 years
    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).
  • Cometsong
    Cometsong over 6 years
    Actually, the only line that needs a <Tab> is the final "EOF" line. The rest of the lines can use spaces. (at least in Bash v4... not sure of earlier.)
  • choroba
    choroba over 6 years
    @Cometsong: It doesn't remove the leading whitespace for me on bash 4.3.42.
  • Cometsong
    Cometsong over 6 years
    @choroba - I just realized I'd added in the awk space-removal before I added that comment! Only the final line needs to be Tab'd using that... :)
  • Tom Hale
    Tom Hale almost 6 years
    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?
  • Admin
    Admin almost 2 years
    Your answer seems incomplete or erroneous. The code is not testable as is. It could be improved with additional supporting information. warning: Stack Exchange doesn' save tabs