Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Indenting heredocs with spaces

For personal development and projects I work on, we use four spaces instead of tabs. However, I need to use a heredoc, and I can't do so without breaking the indention flow.

The only working way to do this I can think of would be this:

usage() {     cat << '    EOF' | sed -e 's/^    //';     Hello, this is a cool program.     This should get unindented.     This code should stay indented:         something() {             echo It works, yo!;         }     That's all.     EOF } 

Is there a better way to do this?

Let me know if this belongs on the Unix/Linux Stack Exchange instead.

like image 239
IBPX Avatar asked Nov 19 '15 22:11

IBPX


People also ask

How do you indent a shell script?

press Ctrl - space at the top of the file. move the cursor to the bottom of the file. press Alt - X and type untabify then return. press Alt - X and type indent-region then return.

How do you end a here document?

To use here-document in any bash script, you have to use the symbol << followed by any delimiting identifier after any bash command and close the HereDoc by using the same delimiting identifier at the end of the text.


1 Answers

(If you are using bash 4, scroll to the end for what I think is the best combination of pure shell and readability.)

For shell scripts, using tabs is not a matter of preference or style; it's how the language is defined.

usage () { ⟶# Lines between EOF are each indented with the same number of tabs ⟶# Spaces can follow the tabs for in-document indentation ⟶cat <<-EOF ⟶⟶Hello, this is a cool program. ⟶⟶This should get unindented. ⟶⟶This code should stay indented: ⟶⟶    something() { ⟶⟶        echo It works, yo!; ⟶⟶    } ⟶⟶That's all. ⟶EOF } 

Another option is to avoid a here document altogether, at the cost of having to use more quotes and line continuations:

usage () {     printf '%s\n' \         "Hello, this is a cool program." \         "This should get unindented." \         "This code should stay indented:" \         "    something() {" \         "        echo It works, yo!" \         "    }" \         "That's all." } 

If you are willing to forego POSIX compatibility, you can use an array to avoid the explicit line continuations:

usage () {     message=(         "Hello, this is a cool program."         "This should get unindented."         "This code should stay indented:"         "    something() {"         "        echo It works, yo!"         "    }"         "That's all."     )     printf '%s\n' "${message[@]}" } 

The following uses a here document again, but this time with bash 4's readarray command to populate an array. Parameter expansion takes care of removing a fixed number of spaces from the beginning of each lie.

usage () {     # No tabs necessary!     readarray message <<'    EOF'         Hello, this is a cool program.         This should get unindented.         This code should stay indented:             something() {                 echo It works, yo!;             }         That's all.     EOF     # Each line is indented an extra 8 spaces, so strip them     printf '%s' "${message[@]#        }" } 

One last variation: you can use an extended pattern to simplify the parameter expansion. Instead of having to count how many spaces are used for indentation, simply end the indentation with a chosen non-space character, then match the fixed prefix. I use :. (The space following the colon is for readability; it can be dropped with a minor change to the prefix pattern.)

(Also, as an aside, one drawback to your very nice trick of using a here-doc delimiter that starts with whitespace is that it prevents you from performing expansions inside the here-doc. If you wanted to do so, you'd have to either leave the delimiter unindented, or make one minor exception to your no-tab rule and use <<-EOF and a tab-indented closing delimiter.)

usage () {     # No tabs necessary!     closing="That's all"     readarray message <<EOF        : Hello, this is a cool program.        : This should get unindented.        : This code should stay indented:        :      something() {        :          echo It works, yo!;        :      }        : $closing EOF     shopt -s extglob     printf '%s' "${message[@]#+( ): }"     shopt -u extglob } 
like image 81
chepner Avatar answered Oct 05 '22 04:10

chepner