7

This question already has an answer here:

If I have some nested and indented statements that echo a multi-line string into a file, the indents from the original shell (in my case bash) will be carried over into the target file.

To avoid this, I remove indents from the echo'ed output, but this loses that indented code formatting in my source, for example,

#! /bin/bash

function configure_default_vhost() {

    case "$2" in
        [--production])
              echo "<VirtualHost *:80>
# These lines are "breaking" out of the preffered indenting
redirect 404 /
ErrorDocument 404
</VirtualHost>
" > /etc/apache/sites-availabe/000-default.conf
    esac
}

I'm aiming to get something as close as possible to this:

#! /bin/bash

function configure_default_vhost() {

    case "$2" in
        [--production])
              echo "<VirtualHost *:80>
                    # These lines are aligned with the preffered indenting
                    redirect 404 /
                    ErrorDocument 404
                    </VirtualHost>
                    " > /etc/apache/sites-availabe/000-default.conf
    esac
}

(Note: this question has been listed as a possible duplicate of a HEREDOC related question. I'm not sure where the correct place to respond to this is so I'll put here for now (someone please let know if otherwise). My question is about indenting code blocks, and heredoc is only one of the many ways to acheive this and actually HEREDOC wasn't the solution I went with.)

marked as duplicate by Mikel, roaima, don_crissti, jasonwryan, slm Apr 24 '15 at 20:08

This question has been asked before and already has an answer. If those answers do not fully address your question, please ask a new question.

2

Using echo with the -e switch ( enable the interpretation of backslash escapes):

#!/bin/bash

function configure_default_vhost() {
    conf_file="/etc/apache/sites-availabe/000-default.conf"
    case "$2" in
        [--production])
            # The ">" overwrites; the ">>" appends. 
            {
                echo -e "<VirtualHost *:80>"
                echo -e "\tredirect 404 /"
                echo -e "\tErrorDocument 404"
                echo -e "</VirtualHost>"
            } > "$conf_file"
    esac
}
  • @StéphaneChazelas I was just about to ask you that in a comment on your answer, the way of capturing mulitple echo or printf seems workable but I wondered how you will capture/group all those lines into the final > file statement. i.e. I dont want to write >> $conf_file on every line. will test {echo; echo; echo; } > conffile now – the_velour_fog Apr 24 '15 at 14:27
  • I think the idea was @StéphaneChazelas - but the format in your answer with the {echo ; echo} worked for me , thanks – the_velour_fog Apr 24 '15 at 16:23
11

You can use a "here-document" with the - modifier. It can be indented by tab characters. You must switch from echo to cat.

cat <<-EOF > /etc/apache/sites-availabe/000-default.conf
        <VirtualHost *:80>
        redirect 404 /
        ErrorDocument 404     
        </VirtualHost>
EOF

Or, to keep tabs in the result, you can pre-process the HERE document by let's say sed and indent with 4 spaces instead:

sed 's/^    //' <<EOF
....{
....(------)func () {
....(------)return
....(------)}
....}
EOF

I used . instead of a space and (------) instead of a tab to show how to format the script.

  • nice one, I will test in a script now, is it possible to add back in tabs using here-document so that the the resulting file can have tabs/indents? I guess the heredoc - modifier only removes a fixed number of indents and therefore you can just write your text with tabs and those tab will naturally be included in output? – the_velour_fog Apr 24 '15 at 14:14
  • @user4668401: Unfortunately, all leading tabs are removed. – choroba Apr 24 '15 at 14:19
  • @user4668401: Check the update. – choroba Apr 24 '15 at 14:27
  • + 1, I tried your sed idea to post process the target file i.e. sed -i /\-----\-----\-------//g and that worked, but with HEREDOC keep getting error: line delimited by end of file wanted EOF, unexpected end of file – the_velour_fog Apr 24 '15 at 16:02
  • 1
    @user4668401: The final EOF must be left aligned, unless you use <<-EOF and indent the final EOF with tabs only. – choroba Apr 24 '15 at 16:12
8

A few approaches:

  • printf '%s\n' > "$conf_file" \
      '<VirtualHost *:80>' \
      '  redirect 404 /' \
      '  ErrorDocument 404' \
      '</VirtualHost>'
    

    If you want escape sequences to be expanded replace %s with %b:

    printf '%b\n' > "$conf_file" \
      '<VirtualHost *:80>' \
      '\tredirect 404 /' \
      '\tErrorDocument 404' \
      '</VirtualHost>'
    
  • @choroba's cat <<- EOF approach, but indentation has to be done with TAB characters only and they're all removed.

  • ...
      indent='
      '; cut -b "${#indent}-" << EOF > "$conf_file"
      <VirtualHost *:80>
        redirect 404 /
        ErrorDocument 404     
      </VirtualHost>
    EOF
    

    (the trick being to have $indent hold the number of characters of indent to trim (beware this time, indenting has to be done with space characters, not tabs)).

  • ...
      cut -d'|' -f2- << EOF > "$conf_file"
      |echo "<VirtualHost *:80>
      |  redirect 404 /
      |  ErrorDocument 404     
      |</VirtualHost>
    EOF
    

    Escape sequences won't be expanded, but variables and command substitutions will. Quote EOF to prevent that (cut... << 'EOF' for instance).

  • You can also trim indentation based on that of the first line:

    show() {
      expand | awk 'NR == 1 {match($0, /^ */); l = RLENGTH + 1}
                    {print substr($0, l)}'
    }
    ...
      show << EOF > "$conf_file"
        <VirtualHost *:80>
          redirect 404 /
          ErrorDocument 404     
        </VirtualHost>
    EOF
    

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