Errata

Learning the bash Shell

Errata for Learning the bash Shell

Submit your own errata for this product.

The errata list is a list of errors and their corrections that were found after the product was released.

The following errata were submitted by our customers and have not yet been approved or disproved by the author or editor. They solely represent the opinion of the customer.

Color Key: Serious technical mistake Minor technical mistake Language or formatting error Typo Question Note Update

Version Location Description Submitted by Date submitted
Printed Page xiv
top of page, Constant Bold section

Used in examples to show interaction between the user and the shell; any text the user types in is shown in Constant Bold. For example:$ pwd/home/cam/adventure/carrol $

should be:

Used in examples to show interaction between the user and the shell; any text the user types in is shown in Constant Bold. For example:
$ pwd
/home/cam/adventure/carrol
$
(only the "pwd" should be bold in that section)

Anonymous  Sep 26, 2013 
Other Digital Version kindle book, location 4082

In Chapter 5 (Flow control) , section 5.2 (for), is written:


The easiest way is to have an initializing
script or function and call the recursive routine from that.
Let's look at this routine.

recls ( )
{
singletab="\t"
for tryfile in "$@"; do
echo $tryfile
if [ -d "$tryfile" ]; then
thisfile=$tryfile
recdir $(command ls $tryfile)
fi
done
unset dir singletab tab
}


...the problem is that the function name is recls, and it supossed to call himself recursively, but the new call is made with recdir.

In a later example the function name is recdir, so the one which is worng is recls.

Juan Eugenio Abadie  Nov 16, 2014 
Other Digital Version kindle book, location 4199

In Chapter 5 (Flow control) , section 5.3 (case), in the nest to last section paragraph, when the author is explaining the code which solves Task 5-4, he wrote:

The problem lies in knowing if sed has performed a substitution or not.

..but "sed" isn't used at all. Here the referenced code:

cd( )
{
case "$#" in
0 | 1) builtin cd $1 ;;
2 ) newdir=${PWD//$1/$2}
case "$newdir" in
$PWD) echo "bash: cd: bad substitution" >&2 ;
return 1 ;;
* ) builtin cd "$newdir" ;;
esac ;;
* ) echo "bash: cd: wrong arg count" 1>&2 ; return 1 ;;
esac
}


Surely at the beggining, the case "2)" was implemented with "sed" but then changed to "Pattern substitution".

Juan Eugenio Abadie  Nov 16, 2014 
Other Digital Version kindle book, location 4277

In Chapter 5 (Flow control) , section 5.5 (while and until), in the first paragraph, the author wrote:

They also resemble analogous constructs in Pascal (while/do and repeat/until) and C (while and do/until).

..but in C the analogous constructor should be "do/while".

Juan Eugenio Abadie  Nov 16, 2014 
Other Digital Version kindle book, location

In Chapter 6 (Command-Line Options and Typed Variables) , section 6.1.3 (getopts), in the third code example, there is a mistake:

if [ -z "$@" ]; then
echo $usage
exit 1
fi

..instead, it must be:

if [ -z "$*" ]; then
echo $usage
exit 1
fi


After this code, there is another mistake but it was already reported and confirmed. i.e.:

#Process the input files
for filename in "$*"; do

...must be:
#Process the input files
for filename in "$@"; do

Juan Eugenio Abadie  Nov 16, 2014 
Other Digital Version chapter 6.3.1 Arithmetic Conditionals
chapter 6.3.1 Arithmetic Conditionals

In chapter 6.3.1 Arithmetic Conditionals, there is a statement " [ \( 3 -gt 2 \) || \( 4 -le 1 \) ] ".

This is a mistake on operators "||" and "&&".
These two operators should take effect on exit statues(of commands or [...] structures). But obviously, " \( 3 -gt 2 \) " does not have a exit statues. So if run this statement, a error will be reported.

The right statement is like below:
[ \( 3 -gt 2 \) ] || [ \( 4 -le 1 \) ] ".

Zilong Deng  Oct 13, 2016 
1
6.3 online edition, 1/2 way down page.

The script code for pushd/popd introducing the "+n" option does not work the same as for the builtins, despite the text saying so. The problem is with how DIR_STACK is rotated. If you use these functions and your stack is like this:
~/one ~/two ~/three ~

Then you execute "pushd +1", you get this:
~/two ~/one ~/three ~

This is not a stack rotation and the builtins don't do this.

In the function pushd(), we have:
...
if [ $(echo $1 | grep '^+[0-9][0-9]*$') ]; then
# case of pushd +n: rotate n-th directory to top
let num=${1#+}
getNdirs $num
DIR_STACK="$target$stackfront$DIR_STACK"
cd $target
echo "$DIR_STACK"
...

The line reassigning DIR_STACK is wrong and should be:
DIR_STACK="$target$DIR_STACK$stackfront"

This fixes the problem.


Robert Clark  Sep 16, 2009 
Printed Page 7
2nd paragraph

Change "has two options and one argument" to "two options and two arguments".

Anonymous   
PDF Page 7
2nd paragraph

This was posted a while ago by another reader but it was not a complete description, which is probably why it is still in the unconfirmed list.

The end of the second sentence is; ...two options and one argument. The beginning of that sentence shows two options and two arguments and the last sentence in the paragraph confirms that there are two options and two arguments in the example in the first sentence.

Sometimes options take their own arguments. For example, lp -d lp1 -h myfile has
two options and one argument. The first option is -d lp1, which means ?Send the
output to the printer (destination) called lp1.? The second option and argument are
the same as in the previous example.

stosss  Apr 23, 2010 
Printed Page 9
Table 1-1

I don't know what page the mistake is on since I'm reading the book online at safari.oreilly.com, but the
mistake is in Table 1-1. The fouth sample command shows "cd/usr/lib", then the fifth sample command shows
that "cd .." will take you to "/home". It will actually take you up one directory to "/lib" instead.

Anonymous   
Printed Page 9
Table 1.1

In the second and third commands, "wonderland" is a file, not a directory.

Porter Scobey  Oct 06, 2017 
Printed Page 9
Table 1-1

According to Fig 1-2 wonderland is a file. In the table both commands
cd book/wonderland and cd ~/book/wonderland are attempting to change
the working directory to a file and will result in a "Not a directory" error.
Table 1-1 indicates the result would change to a nonexistant wonderland
directory.

Stan Moore  Dec 02, 2019 
Printed, PDF, ePub, Page 16
Top

I/O Redirection
cat is short for ?catenate,?

it should be:

I/O Redirection
cat is short for ?concatenate,?

This book has been around for several years and no one has caught this before? I am quite certain it is wrong in all versions and formats(printed and digital).

Anonymous  Nov 19, 2012 
Printed Page 39
2nd paragraph, below table 2-9

I and A are abbreviations for 0i and $a respectively

should be replaced by:

I and A are abbreviations for ^i and $a respectively

Anonymous  Apr 10, 2013 
Printed Page 40
2nd Table

The 2nd table on this page notes the following deletion command shortcuts:
X - Equivalent to dl
x - Equivalent to dh

However, both of these are incorrect. In actuality X is equivalent to dh, and x is equivalent to dl. I have confirmed this on bash version 4.3.42.

Jason Gero  Jan 03, 2016 
PDF Page 41
Table 2-12

There are several mistakes in the table below. There should be some description changes as well for consistency with the editors documentation.

Table 2-12. vi control mode commands for searching the command history
Command Description
k or - Move backward one line
j or + Move forward one line
G Move to line given by repeat count
/string Search backward for string
?string Search forward for string
n Repeat search in same direction as previous
N Repeat search in opposite direction of previous

You have G described in the manner that is correct for j and k. G alone will take you to the bottom of the file. G preceded by a number will take you to the line number that matches the number typed. You have / and ? reversed. / searches forward or down. ? searches backward or up.

According to vi and Vim documentation and "Learning the vi and Vim Editors 7th Edition" the new table 2-12 should be:

Table 2-12. vi control mode commands for searching the command history
Command Description
k or - Move backward or up one line
j or + Move forward or down one line
G Goto line [count], default last line, on the first non-blank character
/string Search forward for string
?string Search backward for string
n Repeat search in same direction as previous
N Repeat search in opposite direction of previous

stosss  Apr 23, 2010 
PDF Page 42
2nd paragraph from bottom of page

The sentence below is talking about using / for searching backward through a file. That is incorrect. It searches forward through a file. The word "backward" in the sentence below needs to be "forward" instead.

To search for "man" again, you can type n, which does another backward search
using the last search string. Typing / again without an argument and hitting
RETURN will accomplish the same thing.

stosss  Apr 24, 2010 
Printed Page 43
3rd paragraph from the bottom

change "2fa" (in bold) to "2Fa"

Anonymous   
PDF Page 43
1st sentence below Table 2-13

Starting with the previous example: let's say you want to change Duchess to Duckess. Make sure that you're at the end of the line (or, in any case, to the left of the h in Duchess); then, if you type Fh, your cursor will move to the h:

should be:

Starting with the previous example: let's say you want to change Duchess to Duckess. Make sure that you're at the end of the line (or, in any case, to the right of the h in Duchess); then, if you type Fh, your cursor will move to the h: The correction: replace left with right. To use Fh you have to be to the right of h

or should be:

Starting with the previous example: let's say you want to change Duchess to Duckess. Make sure that you're at the end of the line (or, in any case, to the left of the h in Duchess); then, if you type fh, your cursor will move to the h: The correction: replace F with f. If you are to the left of h you have to use fh.

stosss  May 05, 2010 
PDF Page 45
Table 2-14

You have twiddle in the first row second column.

Don't you mean tilde?

Table 2-14. Miscellaneous vi-mode commands
Command Description
~ Invert (twiddle) case of current character(s)
- Append last word of previous command, enter input mode
CTRL-L Clear the screen and redraw the current line on it; good for when your screen becomes garbled
# Prepend # (comment character) to the line and send it to the history list; useful for saving a command to be executed later without having to retype it[a]

stosss  May 06, 2010 
Printed Page 50
3rd and 5th paragraphs, and Table 2-18

That which should be printed as a double quote:
"
is instead rendered as a double quote inside a left- and right-caret:
<">

This problem occurs on:
the second double-quote character in line 4 of paragraph 3,
the second and third ones in paragraph 5 line 4,
the first and only double-quote in para. 5 line 5,
and twice again in the 5th (body) line of Table 2-18.



Anonymous   
63
2nd example line in Chapter 3 section "Variables and Quoting"

O'Reilly' Learning's online version (a.k.a. Safari Books Online) has the same issue described in the confirmed errata for the printed verson in Chapter 3, page 63.

2nd example line in the section "Variables and Quoting"

$ fred='Four spaces between these words.'

which has only one space between 'these' and 'words' should read:

$ fred='Four spaces between these words.'

where there are four spaces between 'these' and 'words''.

Alfred Myers  Oct 02, 2019 
Printed Page 82
10th (last) paragraph

The first sentence of this paragraph ends, "... what happens when you type alice &."

It should read, "... what happens when you type source alice &."

The point to this example is to show that these commands are equivalent:
alice,
source alice &

Anonymous   
Printed Page 82
last sentence

This sentence refers to an example of 3 ways to invoke a shell script named "alice"

Item a: source alice
Item b: alice
Item c: source alice &

The last sentence on page 82 begins, "It turns out that the only significant
difference between .c and .b is that you have control of your terminal or workstation
while the command runs"

This sentence should read something like, "it turns out that the only significant
difference between .a and the other two examples is that with the latter two you
retain control of your terminal or workstation while the command runs"

Anonymous   
Printed Page 83
Errata for Figure 4-1.

In this case the correction on the Errata page is wrong and the text of the book is
correct.

Item b in Figure 4-1 is incorrect as the correction states. However, Item c is
correct on page 83. It does not need to be changed.

Item c reads (correctly): source alice &

The purpose of this example is to show that the commands in Item b and Item c are
equivalent. They are:

alice
source alice &

both spawn new shells

Anonymous   
Printed Page 87
5?

The text states that when "$0" is evaluated in a function, it will name that function. It does not. When run in a function, "$0" evaluates to "/pathto/bash".

that is a pretty serious problem, because it renders the entire thrust of this portion of the text false. When run as a function, this code does not evaluate the same way that it does when run as a separate script.

There is a confirmed errata already submitted about this portion of the text. However, I believe that this is a separate issue.

Anonymous  Nov 06, 2015 
Printed Page 93
first paragraph

the phrase that reads "or to -10 if it's not" should read "or to 10 if it's not"

Anonymous   
Printed Page 115
2nd code snippet

(This "error" exists in both the print and Safari versions.)

In the else block of the 2nd code snippet, the code reads:
howmany-${2:--10}

This is correct. However, due to the text rendering used for this code snippet, the "--" is replaced with a single "?" (I think it's an emdash.) This sort of text replacement is very common in word processors.

This text replacement renders the code incorrect, and quite subtle for the reader to detect.

Conveniently, the next paragraph explicitly states what is "supposed" to be occurring. After reconciling the code snippet with the following paragraph, most readers should be able to understand what's going on.

Weston Hunter  Apr 17, 2010 
Printed Page 126
the function body of recls()

In the function body of recls(), the last statement unset three shell variables, which are dir, singletab, and tab. Yet, the shell variable dir is never used in both recls() and recdir().

Why should the shell variable dir be unset?

su  Aug 20, 2023 
Printed Page 126
code block with recls

The code block uses ls and parses the result. This does not handle files or directories with spaces properly.

Alex M  Dec 22, 2023 
Printed Page 131
next to the last line

tab=${tab%\t}

should be
tab=${tab%\\t}

it looks "\" should be escaped in the "pattern".

Song Zhi  Nov 23, 2017 
Printed Page 135
3rd para

I am using version '3.2.25(16)-release' of bash in Cygwin. When running the code in Task 5-6 on page 135 I
get the following error:
./test: line 5: [: to many arguments

The variable $path in needs to be quoted in the while test.

while [ $path ]; do
must be
while [ "$path" ]; do

Anonymous   
Printed Page 139

^-[0-9][0-9]*$ is described as 'an initial dash followed by a digit, optionally followed by one or more digits' which I believe should be '...optionally followed by zero or more digits'

John  Feb 05, 2014 
Printed Page 139
second sample code block

To test the sample script, I created a data file named "albumcount" with the contents shown in the box "Task 4-1".
Issuing either command "./highest albumcount" or "./highest -2 albumcount" produces the expected output.

I renamed the data file "-albumcount". Issuing either command "./highest -2 -albumcount" or "./highest -albumcount" produces an error message.

The following script avoids these errors.

usageMessage="usage: highest [-N] filename"
if [ ${#} -eq 2 ]
then
if [ -n "$(echo ${1} | grep '^-[0-9][0-9]*$')" ]; then
howmany=$1
shift
else
echo $usageMessage
exit 1
fi
elif [ ${#} -eq 1 ]
then
howmany="-10"
else
echo $usageMessage
exit 2
fi
filename=$1
sort -nr -- $filename | head $howmany

Eric Hyatt  Jul 19, 2016 
Printed Page 148
1st paragraph after the Table 6-2

Minus (-) is used in place of unary decrement operator(--) in the following sentence:
The ++ and - operators are useful when you want to increment or decrement a value by one.

bbhanuprakash  Nov 10, 2010 
Printed Page 149
Paragraph following Table 6-4

In the compound expressions on the third line of this paragraph, the "||" operator is incorrect and should be "-o", and the "&&" operator is incorrect and should be "-a".

[ \( 3 -gt 2 \) || \( 4 -le 1 \) ] ---> [ \( 3 -gt 2 \) -o \( 4 -le 1 \) ]

[ \( 3 -gt 2 \) && \( 4 -le 1 \) ] ---> [ \( 3 -gt 2 \) -a \( 4 -le 1 \) ]

Anonymous  Aug 21, 2012 
Printed Page 149
Bottom paragraph.

While the test expression in this paragraph is technically correct, it is comparing the arithmetic expression to the _string_ "1". Since this is a discussion of arithmetic tests, this should use "-eq" to compare to the _value_ 1 instead.

[ $(((3>2) && (4<=1))) = 1 ] ---> [ $(((3>2) && (4<=1))) -eq 1 ]

Anonymous  Aug 21, 2012 
Printed Page 150
Example following third paragraph

The example "let intvar=expression" is technically correct but it misses the point. The left-hand side of the "let" does not need to be an integer variable declared with "declare -i", which is what the example implies. Any variable will do here. All the following are equivalent:

let var=expression

var=$((expression))

declare -i var
var=expression

Except for the use of whitespace in the expression, which is only tolerated inside the arithmetic evaluator $(( )).

Anonymous  Aug 21, 2012 
Printed Page 152
sample code

The first line of the sample code is
.ps 8
This looks like it escaped from a troff input file. It should be removed.

The first two lines of function pushd are
dirname=$1 if [ -n $dirname ] && [ \( -d $dirname \) -a
\( -x $dirname \) ]; then

This doesn't parse. The two lines should read
dirname=$1
if [ -n $dirname ] && [ \( -d $dirname \) -a \( -x $dirname \) ]; then

Eric Hyatt  Jul 20, 2016 
Printed Page 153
final code block

To emulate the behavior of the actual pushd function, the assignment

DIR_STACK="$target$stackfront$DIR_STACK" should be changed to DIR_STACK="$target$DIR_STACK$stackfront"

Oliver Albertini  Jan 15, 2017 
Printed Page 156
3rd paragraph

The following sentence appears to be invalid with respect to bash version 3.1(atleast).
"When initialisation id true the loop then evaluates ending condition."

Arithmetic for loop doesn't consider the initialisation section for evaluating to enter "ending section" or the body of the loop.

for example, the following loops echo the message:
for ((0;;))
do
echo "initialisation with zero condition"
break
done

for ((1;;))
do
echo "initialisation with condition 1"
break
done

bbhanuprakash  Nov 12, 2010 
Printed Page 160
sample code

the sample code is
if [ ${values[j]} -le ${values[$lowest]}]; then
that line should be changed to
if [ ${values[j]} -lt ${values[$lowest]}]; then
For 2 reasons: 1) avoid unnecessary work
and 2) match the description in the 3rd following paragraph
"if a value is less then <em>lowest</em> is set''

Eric Hyatt  Jul 08, 2016 
Printed Page 163
Table 7-1

2 rows in the table should be deleted.
The problematic redirectors are n>&m and n<&m.
Problem 1: Neither definition mentions m.
Problem 2: These redirectors are special cases of 2 other redirectors in the table, n>&word and n<&word.

Eric Hyatt  Jul 11, 2016 
Printed Page 167
3rd paragraph starting with "The second variation..."

This is not technically an error, but rather a point of confusion. When using a
heredoc, with the variation <<- the ending label can also be indented. Unfortunately,
your example format does not point this out. EOF starts in column1.

It's not just newbies that are confused about this. For example, the CD-ROM that
Prentice Hall distributes with their book "Unix Shells by example" Fourth edition

Anonymous   
Printed Page 173
4th code example

The example code starts "$ read" and ends "duchess". This sample is a mess. Using HTML-like tags, I think the whole example should read:

<cw>$</cw> <cb>read character1 character2</cb>
<cw>alice duchess
$</cw> <cb>echo $character1</cb>
<cw>alice
$</cw> <cb>echo $character2</cb>
<cw>duchess

Eric Hyatt  Jul 12, 2016 
Printed Page 179
6

It's expected an example using the -d option, but the code uses the -s option instead

Leandro Meili  Sep 12, 2019 
Printed Page 183
3rd paragraph after Quoting

In the text:
"Double quotes ("") bypass Steps 1 through 4"

should be

"Double quotes ("") bypass Steps 1 through 5"

Alexandru Cardaniuc  Aug 29, 2010 
Printed Page 189
code listing after second paragraph

[ $(grep \t* $cmd) ]
This has two major errors related to grep. One is that you can't simply pass \t to grep either in a script or from the command line. The other is that cmd is not a file but a command line read from the rules file. Grep either processes a file given as a command argument or stdin; it does not process a string passed as an argument. A minor error is that you should be looking for tab as the first character, not simply any tab on the line. I propose the following replacement:
[ "$( echo $cmd | grep $'^\t' )" ]

David McCracken  Jun 26, 2009 
Printed Page 189
code listing after second paragraph

In 2009 a bug report was submitted for this function. After implementing that bug fix, the function still did not work. makecmd requires variable cmd to begin with a TAB, but since the default value of IFS includes a TAB, no variable that bash reads will include a TAB until IFS is redefined. The following function worked for me.

makecmd ()
{
read target colon sources
IFS=' '
for src in $sources; do
if [ $src -nt $target ]; then
while read cmd && echo "$cmd" | grep -q $'^\t' ; do
echo "${cmd#$'\t'}"
eval ${cmd#$'\t'}
done
fi
done
}

Eric Hyatt  Jul 23, 2016 
Printed Page 208
End of the page.

trap "echo 'You hit control-C!'" INT TERM
results in the following message:

-bash: !'": event not found

So I tried escaping the ! using \, it registered the trap.
trap "echo 'You hit control-C\!'" INT TERM

But I could not see the echo message when I hit ^C during the while loop discussed in the same page.

I'm using the following version of bash:
GNU bash, version 3.1.17(1)-release (i586-suse-linux)

bbhanuprakash  Nov 18, 2010 
Printed Page 223
2nd code sample

The first line says ".ps 8". Omit this line.
The code sample seems to rely on an alias. To simplify the example, the following does not rely on an alias. The code sample should read:

<cw>$</cw> <cb>set -o xtrace</cb>
<cw>$</cw> <cb>alice=girl</cb>
<cw>+ alice=girl
$</cw> <cb>echo "$alice"</cb>
<cw>+ echo girl
girl
$</cw> <cb>ls -l $(type -path vi)</cb>
<cw>++ type -path vi
+ ls -l /usr/bin/vi
lrwxrwxrwx 1 root root 20 Jul 21 2015 /usr/bin/vi -> /etc/alternatives/vi
$

Eric Hyatt  Jul 28, 2016 
Printed Page 223
3rd code sample

The first line says ".ps 8". Omit this line.
The given sample has some typographical errors. The output of its "ls" command shows that the issued command must have been "ls -F". The code sample should read:

<cw>$</cw> <cb>ls -l $(type -path vi)</cb>
<cw>xxtrace--> type -path vi
xtrace--> ls -l /usr/bin/vi
lrwxrwxrwx 1 root root 20 Jul 21 2015 /usr/bin/vi -> /etc/alternatives/vi
$

Eric Hyatt  Jul 29, 2016 
Printed Page 224
5th paragraph

This paragraph talks about the noexec option:

"The last option is noexec, which reads in the shell script and checks for syntax
errors, but doesn't execute anything. It's worth using if your script is
syntactically complex (lots of loops, command blocks, string operators, etc.) and the
bug has side effects (like creating a large file or hanging up the system)."

The problem is that the last sentence is missing the single word: "also" which should
be inserted after the word "It's". This is actually a serious omission because it
totally changes the reader's perception of the command (or at least this
reader's perception).

Without that word, the sentence sounds like the noexec option is only useful for
detecting logical errors, when in fact I believe the primary reason for the option is
to detect syntactical errors. I believe a secondary reason is to monitor
program flow which can be used to detect logical errors, such as the author
mentioned. However, the lack of one word totally changes the meaning of the sentence
to me.

Anonymous   
Printed Page 237
Code example in paragraph 'Breakpoints'

The following line in function _setbp:

elif [ $(echo $1 | grep '^[0-9]*') ]; then

should be

elif [ $(echo $1 | grep '^[0-9][0-9]*') ]; then

Anonymous   
Printed, PDF Page 274
4th Paragraph

This paragraph: But if you want to be absolutely sure, simply search through all shell scripts in all directories in your PATH.
An easy way to perform the search is to use the file command, which we saw in Chapter 5, and Chapter 9. file
prints "executable shell script" when given the name of one. [2]Here is a script that looks for ^ in shell scripts
in every directory in your PATH:

Suggests the "file" command was discussed in those chapters. The file attribute "test" was described but I could find no documentation in those chapters on the file command itself. I looked in my book and the pdf version. Thank you.

Anonymous  Mar 25, 2014 
Printed Page 299
Table B-9

The 12th row of the table concerns the redirector ">>file". The same redirector appears in the 4th row of the table. The function of the redirector in the 12th row refers to "file descriptor n". To make sense of this, the redirector in the 12th row should be changed to

n>>file

Eric Hyatt  Jul 30, 2016 
PDF Page 351
Task 5-3, the FOR construct

When you run recls *, every directory with spaces in name, like "my dir", is not visited by program, and the output is that you have 2 files (my and dir) instead of directory "my dir"

Alex  Oct 25, 2017 
Printed Page 013147572X
contains a related error which prevents their examples from working.

i.e. see /quigley_unix_files/chap14/Ex_14.56-14.68/mainprog. The standard variation
<< is used instead of <<- but the ending terminator is indented causing the rest of
the script to be echoed.

Their discussion of here docs and example 13.89 (pp. 852) points out that the ending
label as well as the input can be indented as long as the <<- format is used.

Anonymous