11

The following codepiece is a script used to install Apache. I run this script in-place when executing it from the heredoc block that wraps it (APACHE).

Note that inside this APACHE heredoc, I have an internal heredoc (MOD_REWRITE), which I can refer to as a "secondary" or "internal" heredoc.

Please also note that all the code inside APACHE is indented (tabulated), besides the code of the internal heredoc.

bash /dev/fd/10 10<<'APACHE'

    # Setup basics:

    apt-get update -y && apt-get upgrade -y
    apt-get install tree zip unzip
    a2enmod mcrypt && a2enmod mbstring

    # Setup LAMP environment with enabled mod rewrite:

    echo -e "\07" && echo -e "\077" # Insert password.
    apt-get install lamp-server^ -y
    a2enmod rewrite

cat <<MOD_REWRITE >> /etc/apache2/apache2.conf

<Directory /var/www/>
Options Indexes FollowSymLinks
AllowOverride All
Require all granted
</Directory>
MOD_REWRITE

    systemctl restart apache2.service

    # Setup maldet:

    cd /usr/local/src
    wget http://www.rfxn.com/downloads/maldetect-current.tar.gz && tar -xzf maldetect-current.tar.gz
    cd maldetect-* && bash ./install.sh

APACHE

If I indent it with commands with spaces instead of tabulations, I can run the script just fine (as long as it doesn't have the MOD_REWRITE inside it). If I add the MOD_REWRITE, the script brakes when executed; The same happens if I remove all space-indents whatsoever and totally replace them with tabulations, but AFAIK, the last time I tried to execute the script with tabulations, it also broke (even when I added an hyphen between bash /dev/fd/10 10<< and 'APACHE'.

My question:

What is the right way to indent the MOD_REWRITE heredoc inside the APACHE heredoc, so the script would be more unified and would execute without breakage?

Notes:

  • The reason I want to indent internal heredocs as well, just as I would do with any other command, is from aesthetic reasons --- It makes it easier for me to read and organize my scripts.

  • This question is not the same as "Can't indent heredoc to match nesting's indent" because it asks about the correct way of indenting internal heredocs inside external heredocs, and not about indenting external heredocs themselves.

5
  • It's not the question you asked, but for one fairly short line you could use a herestring instead of a heredoc, or just a printf command (or echo but less recommended) Mar 22, 2017 at 17:12
  • Please edit your question and clarify a bit. For example, "I can paste space-indented commands and run them in an heredoc just fine, the last time I tried that for heredocs" is not very clear. Ideally, you should show us an example of what you would like to have, highlighting which heredoc is actually the problem, and then show us the error you get with it. The screenshot isn't really relevant since we can't see that it's nested, and we can't copy anything out of it to test anyway.
    – terdon
    Mar 23, 2017 at 9:23
  • I thank you dearly for this feedback and have edited the question carefully to better clarify my aim.
    – user149572
    Mar 23, 2017 at 10:19
  • Why does the script need to be in a here document anyway? The outer here document seems rather superfluous.
    – tripleee
    Apr 19, 2017 at 10:38
  • It uses me for better organizing the script and allow indentations before coping and executing.
    – user149572
    Apr 19, 2017 at 10:41

2 Answers 2

22

A here-document is a redirection of the form:

<<[-]DELIMITER
    ....
    ....
    ....
DELIMITER

The optional - (inside the brackets above) changes the way the delimiter is matched and allows indenting each line inside the heredoc content, with tabulations (no spaces allowed).

  • "Matched" means the delimiter is matched to the opener (as when DELIMITER matches <<DELIMITER or <<-DELIMITER, for example).

  • Note that you may use one or more spaces between << or <<-, and the word that follows).

So to sum up the basic laws for matching inside a singlar heredoc:

  1. The opener must be placed at the very beginning of the line in an applicable syntax.
  2. The delimiter must be the only word of its line.
  3. All content under the opener (including the delimiter) can be indented with any number of tabulations, with the <<-DELIMITER syntax.

Since with the former syntax, no blanks can precede the heredoc opener, if you want to indent it, your only choice is to use the following syntax and you must exclusively use tabulations at the beginning of each line inside the heredoc's content.

Now you have two options with the <<- syntax.

First option

Use the <<- syntax for the inner heredoc.

bash << APACHE
    ... 
    ... 
    cat <<- MOD_REWRITE
⇨       ...     
⇨       ....    
⇨       MOD_REWRITE
    ... 
    ... 
APACHE

(indentation is 4 spaces, tabulations are symbolized with )

The code seen by bash will be exactly what is written on your screen (i.e. bash will see the indentation of each line as you see it now). When the inner heredoc is met, owing to the <<- syntax, bash will strip the tabulation characters leading each line until the MOD_REWRITE delimiter.

Second option

Use the <<- syntax for the outer heredoc.

bash <<- APACHE
⇨       ...
⇨       ...
⇨       cat << MOD_REWRITE
⇨       ⇨       ...
⇨       ⇨       ....
⇨       MOD_REWRITE
⇨       ...
⇨       ...
APACHE

This time, the code seen by bash will differ from what you see: it won't contain any leading tabulation. That's why this is not a problem that I use the << syntax for the inner heredoc: the MOD_REWRITE delimiter will be at the beginning of the line.

In both cases, the MOD_REWRITE delimiter is recognized and your Apache configuration file /etc/apache2/apache2.conf is not indented. If you want to indent parts of it, your only option is to use spaces (after the initial tabulations that will be stripped).

Of course, there is a third option: to use the <<- syntax for both heredocs, but that won't change anything from option 2 since all the leading tabulations are removed when the code is sent to bash.

3
  • 1
    Thanks @Benia. I have changed your note into an introduction to the heredoc syntax. Moreover, I have rephrased the sentence "your only choice is to use the second syntax". Hope it is clearer for you.
    – xhienne
    Mar 29, 2017 at 14:02
  • @Benia There is at least one error, and undesired change of wording. I have no time for this non-trivial edit. Be patient, I will come back later and review this.
    – xhienne
    Mar 29, 2017 at 15:09
  • Note regarding users who edit Nix files from Windows programs: If you did everything described by xhienne but still couldn't get this to work and had the error containing "delimited by end-of-file", please see this QA session: unix.stackexchange.com/questions/359910/…
    – user149572
    Apr 20, 2017 at 4:49
5
+100

First of all, indenting the cat line and the subsequent lines (except the line with the NGINX terminator) should work. The leading indentation within the document will be preserved in the output, which may or may not be an issue in your case (if the output is indeed HTML, there is a chance it does not matter if extra whitespace is introduced).

Also, the Bash documentation says this:

The format of here-documents is:

       <<[-]word
               here-document
       delimiter

[...]

If the redirection operator is <<-, then all leading tab characters are
stripped from input lines and  the  line  containing  delimiter.   This
allows  here-documents within shell scripts to be indented in a natural
fashion.

So you could use <<-'PHP_FPM' and indent each of the lines that follow with tabs, which would get stripped by the redirection.

6
  • BTW, where did you get your knowledge of heredocs? Can you recommend me of a book on this subject ?
    – user149572
    Mar 22, 2017 at 15:34
  • 2
    @Benia The official documentation (gnu.org/software/bash/manual/bash.html) or 3rd party sites (wiki.bash-hackers.org/syntax/redirection#here_documents).
    – phk
    Mar 22, 2017 at 16:42
  • I am very sorry and I believe a minor edit could help all readers here - I think I might have explained myself in a bad way in the question: I am not asking about indenting a heredoc, but rather indenting a heredoc inside an heredoc. I want to indent only the second heredoc (the internal one). If I understand correct, only the first one must include an hyphen between the operator and the word; The second (internal) shouldn't have that hyphen and we could indent it only with tabs.
    – user149572
    Mar 23, 2017 at 5:31
  • I would thank you if you could example the code from my question inside the answer because the answer would best example the case of heredoc inside an heredoc for newcomers who knows nothing about this. I would gladly give bounty if I could but I can't because it was marked as duplicate.
    – user149572
    Mar 23, 2017 at 5:33
  • 1
    Hi, I should have awarded the 100 bounty to xhienne which his answer worked for me. I mistakenly gave it to you (sorry if it sounds awful) and can't undo my action. Can you please offer 100 bounty on this question and give to it xhienne? Thanks anyway,
    – user149572
    Mar 30, 2017 at 9:50

You must log in to answer this question.