80609f6010
included commits to RCS files with non-trunk default branches.
546 lines
20 KiB
Plaintext
546 lines
20 KiB
Plaintext
$OpenBSD: NOTES,v 1.8 2003/02/26 03:53:35 david Exp $
|
|
|
|
General features of at&t ksh88 that are not (yet) in pdksh:
|
|
- exported aliases and functions (not in ksh93).
|
|
- set -t.
|
|
- signals/traps not cleared during functions.
|
|
- trap DEBUG, local ERR and EXIT traps in functions.
|
|
- ERRNO parameter.
|
|
- doesn't have posix file globbing (eg, [[:alpha:]], etc.).
|
|
- use of an `agent' to execute unreadable/setuid/setgid shell scripts
|
|
(don't ask).
|
|
- read/select aren't hooked in to the command line editor
|
|
- the last command of a pipeline is not run in the parent shell
|
|
|
|
Known bugs (see also BUG-REPORTS and PROJECTS files):
|
|
Variable parsing, Expansion:
|
|
- some specials behave differently when unset (eg, IFS behaves like
|
|
" \t\n") others lose their special meaning. IFS/PATH taken care of,
|
|
still need to sort out some others (eg, TMOUT).
|
|
Parsing,Lexing:
|
|
- line numbers in errors are wrong for nested constructs. Need to
|
|
keep track of the line a command started on (can use for LINENO
|
|
parameter as well).
|
|
- a $(..) expression nested inside double quotes inside another $(..)
|
|
isn't parsed correctly (eg, $(echo "foo$(echo ")")") )
|
|
Commands,Execution:
|
|
- setting special parameters that have side effects when
|
|
changed/restored (ie, HISTFILE, OPTIND, RANDOM) in front
|
|
of a command (eg, HISTFILE=/foo/bar echo hi) effects the parent
|
|
shell. Note that setting other (not so special) parameters
|
|
does not effect the parent shell.
|
|
- `echo hi | exec cat -n' causes at&t to exit, `exec echo hi | cat -n'
|
|
does not. pdksh exits for neither. Don't think POSIX requires
|
|
an exit, but not sure.
|
|
- `echo foo | read bar; echo $bar' prints foo in at&t ksh, nothing
|
|
in pdksh (ie, the read is done in a separate process in pdksh).
|
|
Misc:
|
|
|
|
Known problems not caused by ksh:
|
|
- after stoping a job, emacs/vi is not re-entered. Hitting return
|
|
prints the prompt and everything is fine again. Problem (often
|
|
involving a pager like less) is related to order of process
|
|
scheduling (shell runs before `stop'ed (sub) processes have had a chance
|
|
to clean up the screen/terminal).
|
|
|
|
Known differences between pdksh & at&t ksh (that may change)
|
|
- vi:
|
|
- `^U': at&t: kills only what has been inserted, pdksh: kills to
|
|
start of line
|
|
- at&t ksh login shells say "Warning: you have running jobs" if you
|
|
try to exit when there are running jobs. An immediate second attempt
|
|
to exit will kill the jobs and exit. pdksh does not print a warning,
|
|
nor does it kill running jobs when it exits (it does warn/kill for
|
|
stopped jobs).
|
|
- TMOUT: at&t prints warning, then waits another 60 seconds. If on screwed
|
|
up serial line, the output could cause more input, so pdksh just
|
|
prints a message and exits. (Also, in at&t ksh, setting TMOUT has no
|
|
effect after the sequence "TMOUT=60; unset TMOUT", which could be
|
|
useful - pdksh may do this in the future).
|
|
- in pdksh, if the last command of a pipeline is a shell builtin, it is
|
|
not executed in the parent shell, so "echo a b | read foo bar" does not
|
|
set foo and bar in the parent shell (at&t ksh will).
|
|
This may get fixed in the future, but it may take a while.
|
|
- in pdksh, set +o lists the options that are currently set, in at&t ksh
|
|
it is the same as set -o.
|
|
- in pdksh emacs mode, ^T does what gnu emacs does, not what at&t ksh
|
|
does.
|
|
- in ksh93, `. name' calls a function (defined with function) with POSIX
|
|
semantics (instead of ksh semantics). in pdksh, . does not call
|
|
functions.
|
|
- test: "test -f foo bar blah" is the same as "test -f foo" (the extra
|
|
arguments, of which there must be at least 2, are ignored) - pdksh
|
|
generates an error message (unexpected operator/operand "bar") as it
|
|
should. Sometimes used to test file globs (e.g., if test -f *.o; ...).
|
|
- if the command 'sleep 5 && /bin/echo blah' is run interactively and
|
|
is the sleep is stopped (^Z), the echo is run immediately in pdksh.
|
|
In at&t ksh, the whole thing is stopped.
|
|
- LINENO:
|
|
- in ksh88 variable is always 1 (can't be changed) in interac mode;
|
|
in pdksh it changes.
|
|
- Value of LINENO after it has been set by the script in one file
|
|
is bizarre when used in another file.
|
|
|
|
Known differences between pdksh & at&t ksh (that are not likely to change)
|
|
- at&t ksh seems to catch or ignore SIGALRM - pdksh dies upon receipt
|
|
(unless it's traped of course)
|
|
- typeset:
|
|
- at&t ksh overloads -u/-l options: for integers, means unsigned/long,
|
|
for strings means uppercase/lowercase; pdksh just has the
|
|
upper/lower case (which can be useful for integers when base > 10).
|
|
unsigned/long really should have their own options.
|
|
- at&t ksh can't have justified integer variables
|
|
(eg, typeset -iR5 j=10), pdksh can.
|
|
- in pdksh, number arguments for -L/-R/-Z/-i must follow the option
|
|
character, at&t allows it at the end of the option group (eg,
|
|
at&t ksh likes "typeset -iu5 j", pdksh wants "typeset -i5 -u j"
|
|
or "typeset -ui5 j"). Also, pdksh allows "typeset -i 5 j" (same
|
|
as "typeset -i5 j"), at&t ksh does not allow this.
|
|
- typeset -R: pdksh strips trailing space type characters (ie,
|
|
uses isspace()), at&t ksh only skips blanks.
|
|
- at&t ksh allows attributes of read-only variables to be changed,
|
|
pdksh allows only the export attribute to be set.
|
|
- (some) at&t ksh allows set -A of readonly variables, pdksh does not.
|
|
- at&t ksh allows command assignments of readonly variables (eg, YY=2 cat),
|
|
pdksh does not.
|
|
- at&t ksh does not exit scripts when an implicit assignment to an integer
|
|
variable fails due to an expression error: eg,
|
|
echo 2+ > /tmp/x
|
|
unset x; typeset -i x
|
|
read x < /tmp/x
|
|
echo still here
|
|
prints an error and then prints "still here", similarly for
|
|
unset x; typeset -i x
|
|
set +A x 1 2+ 3
|
|
echo still here
|
|
and
|
|
unset x y; typeset -i x y; set +A y 10 20 30
|
|
set +A x 1 1+y[2+] 3
|
|
echo still here
|
|
pdksh exits a script in all the above cases. (note that both shells
|
|
exit for:
|
|
unset x; typeset -i x
|
|
for x in 1 2+ 3; do echo x=$x; done
|
|
echo still here
|
|
).
|
|
- at&t ksh seems to allow function calls inside expressions
|
|
(eg, typeset -i x='y(2)') but they do not seem to be regular functions
|
|
nor math functions (eg, pow, exp) - anyone known anything about this?
|
|
- `set -o nounset; unset foo; echo ${#foo}`: at&t ksh prints 0; pdksh
|
|
generates error. Same for ${#foo[*]} and ${#foo[@]}.
|
|
- . file: at&t ksh parses the whole file before executing anything,
|
|
pdksh executes as it parses. This means aliases defined in the file
|
|
will affect how pdksh parses the file, but won't affect how at&t ksh
|
|
parses the file. Also means pdksh will not parse statements occuring
|
|
after a (executed) return statement.
|
|
- a return in $ENV in at&t ksh will cause the shell to exit, while in
|
|
pdksh it will stop executing the script (this is consistent with
|
|
what a return in .profile does in both shells).
|
|
- at&t ksh does file globbing for `echo "${foo:-"*"}"`, pdksh does not
|
|
(POSIX would seem to indicate pdksh is right).
|
|
- at&t ksh thinks ${a:##foo} is ok, pdksh doesn't.
|
|
- at&t does tilde expansion on here-document delimiters, pdksh does
|
|
not. eg.
|
|
$ cat << ~michael
|
|
~michael
|
|
$
|
|
works for pdksh, not for at&t ksh (POSIX seems to agree with pdksh).
|
|
- in at&t ksh, tracked aliases have the export flag implicitly set
|
|
and tracked aliases and normal aliases live in the same name space
|
|
(eg, "alias" will list both tracked and normal aliases).
|
|
in pdksh, -t does not imply -x (since -x doesn't do anything yet), and
|
|
tracked/normal aliases live in separate name spaces.
|
|
in at&t ksh, alias accepts + options (eg, +x, +t) - pdksh does not.
|
|
in pdksh, alias has a -d option to allow examination/changing of
|
|
cached ~ entries, also unalias has -d and -t options (unalias -d
|
|
is useful if the ~ cache gets out of date - not sure how at&t deals
|
|
with this problem (it does cache ~ entries)).
|
|
- at&t ksh will stop a recursive function after about 60 calls; pdksh
|
|
will not since the limit is arbitrary and can't be controlled
|
|
by the user (hit ^C if you get in trouble).
|
|
- the wait command (with and without arguments) in at&t ksh will wait for
|
|
stopped jobs when job control is enabled. pdksh doesn't.
|
|
- at&t ksh automatically sets the bgnice option for interactive shells;
|
|
pdksh does not.
|
|
- in at&t ksh, "eval `false`; echo $?" prints 1, pdksh prints 0 (which
|
|
is what POSIX says it should). Same goes for "wait `false`; echo $?".
|
|
(same goes for "set `false`; echo $?" if posix option is set - some
|
|
scripts that use the old getopt depend on this, so be careful about
|
|
setting the posix option).
|
|
- in at&t ksh, print -uX and read -uX are interrperted as -u with no
|
|
argument (defaults to 1 and 0 respectively) and -X (which may or
|
|
may not be a valid flag). In pdksh, -uX is interpreted as file
|
|
descriptor X.
|
|
- in at&t ksh, some signals (HUP, INT, QUIT) cause the read to exit, others
|
|
(ie, everything else) do not. When it does cause exiting, anything read
|
|
to that point is used (usually an empty line) and read returns with 0
|
|
status. pdksh currently does similar things, but for TERM as well and
|
|
the exit status is 128+<signal-number> - in future, pdksh's read will
|
|
do this for all signals that are normally fatal as required by POSIX.
|
|
(POSIX does not require the setting of variables to null so applications
|
|
shouldn't rely on this).
|
|
- in pdksh, ! substitution done before variable substitution; in at&t ksh
|
|
it is done after substitution (and therefor may do ! substitutions on
|
|
the result of variable substitutions). POSIX doesn't say which is to be
|
|
done.
|
|
- pwd: in at&t ksh, it ignores arguments; in pdksh, it complains when given
|
|
arguments.
|
|
- the at&t ksh does not do command substition on PS1, pdksh does.
|
|
- ksh93 allows ". foo" to run the function foo if there is no file
|
|
called foo (go figure).
|
|
- field splitting (IFS): ksh88/ksh93 strip leading non-white space IFS
|
|
chars, pdksh (and POSIX, I think) leave them intact. e.g.
|
|
$ IFS="$IFS:"; read x; echo "<$x>"
|
|
::
|
|
prints "<>" in at&t ksh, "<::>" in pdksh.
|
|
- command completion: at&t ksh will do completion on a blank line (matching
|
|
all commands), pdksh does not (as this isn't very useful - use * if
|
|
you really want the list).
|
|
- co-processes: if ksh93, the write portion of the co-process output is
|
|
closed when the most recently started co-process exits. pdksh closes
|
|
it when all the co-processes using it have exited.
|
|
- pdksh accepts empty command lists for while and for statements, while
|
|
at&t ksh (and sh) don't. Eg., pdksh likes
|
|
while false ; do done
|
|
but ksh88 doesn't like it.
|
|
- pdksh bumps RANDOM in parent after a fork, at&t ksh bumps it in both
|
|
parent and child:
|
|
RANDOM=1
|
|
echo child: `echo $RANDOM`
|
|
echo parent: $RANDOM
|
|
will produce "child: 16838 parent: 5758" in pdksh, while at&t ksh
|
|
will produce "child: 5758 parent: 5758".
|
|
|
|
Oddities in ksh (pd & at&t):
|
|
- array references inside (())/$(()) are strange:
|
|
$(( x[2] )) does the expected, $(( $x[2] )) doesn't.
|
|
- `typeset -R3 X='x '; echo "($X)"` produces ( x) - trailing
|
|
spaces are stripped.
|
|
- typeset -R turns off Z flag.
|
|
- both shells have the following mis-feature:
|
|
$ x='function xx {
|
|
cat -n <<- EOF
|
|
here we are in xx
|
|
EOF
|
|
}'
|
|
$ (eval "$x"; (sleep 2; xx) & echo bye)
|
|
[1] 1234
|
|
bye
|
|
$ xx: /tmp/sh1234.1: cannot open
|
|
- bizarre special handling of alias/export/readonly/typeset arguments
|
|
$ touch a=a; typeset a=[ab]; echo "$a"
|
|
a=[ab]
|
|
$ x=typeset; $x a=[ab]; echo "$a"
|
|
a=a
|
|
$
|
|
- both ignore SIGTSTP,SIGTTIN,SIGTTOU in exec'd processes when talking
|
|
and not monitoring (at&t ksh kind of does this). Doesn't really make
|
|
sense.
|
|
(Note that ksh.att -ic 'set +m; check-sigs' shows TSTP et al aren't
|
|
ignored, while ksh.att -ic 'set +m^J check-sigs' does... very strange)
|
|
- when tracing (set -x), and a command's stderr is redirected, the trace
|
|
output is also redirected. so "set -x; echo foo 2> /tmp/O > /dev/null"
|
|
will create /tmp/foo with the lines "+ > /dev/null" and "+ echo foo".
|
|
- undocumented at&t ksh feature: FPATH is searched after PATH if no
|
|
executable is found, even if typeset -uf wasn't used.
|
|
|
|
at&t ksh bugs:
|
|
[various versions:
|
|
MIPS m120 RISC/os 5.0: Version 11/16/88d
|
|
Dec alpha osf/1 v1.3: OSF/1 Version 11/16/88d NLS
|
|
HP pa HP-UX 9.01: Version 11/16/88
|
|
]
|
|
- (only hpux)
|
|
$ _[2]=hi
|
|
Bus error (core dumped)
|
|
- (only riscos, hpux)
|
|
$ typeset x[
|
|
$
|
|
- (only osf/1)
|
|
$ A=B cat << EOF
|
|
.$A.
|
|
EOF
|
|
Segmentation fault(coredump)
|
|
$
|
|
- (only osf/1)
|
|
$ read "?foo "
|
|
foo Foo
|
|
$ set | grep Foo
|
|
=Foo
|
|
$
|
|
- (all)
|
|
$ typeset -i A
|
|
$ typeset -L3 A
|
|
$ typeset -l A
|
|
Illegal instruction (core dumped)
|
|
- (all)
|
|
$ for i in a b c ; do echo $i, ${i[2]}, ${i[10]} ; done
|
|
a, ,
|
|
a, , b
|
|
a, , c
|
|
$
|
|
- (all)
|
|
$ echo ${abc:-G { I } K }
|
|
G { I K }
|
|
$
|
|
$ abc=hi
|
|
$ echo ${abc:-G { I } K }
|
|
hi K }
|
|
$
|
|
The second echo should only have printed `hi'.
|
|
- (all)
|
|
$ echo ${abc:- > foo}
|
|
syntax error: > unexpected
|
|
$
|
|
- (all? hpux) read reads too much from pipe (when pipe isn't stdin)
|
|
print 'hi\nthere' | ksh 8<&0 0< /dev/tty
|
|
$ read -u8 x
|
|
$ print $x
|
|
hi
|
|
$ cat 0<&8
|
|
$ read -u8 y
|
|
$ print $y
|
|
there
|
|
$
|
|
- (all)
|
|
$ umask 0
|
|
$ umask
|
|
00
|
|
$
|
|
- (osf, mips, !hpux)
|
|
$ exec alias
|
|
alias: not found
|
|
(shell dead)
|
|
- (all) non-white space IFS in non-substitution not preserved
|
|
$ IFS="$IFS:"
|
|
$ echo : "$@" # this is ok
|
|
:
|
|
$ echo :"$@" # this should print : too (me thinks)
|
|
|
|
$
|
|
- (only osf/1)
|
|
$ set +m
|
|
$ sleep 1 & # wait for a sec or two
|
|
$ jobs
|
|
Memory fault (core dumped)
|
|
- (all)
|
|
$ (sleep 1 & echo hi) &
|
|
[1] 123
|
|
$ [1] 234
|
|
hi
|
|
- (osf/1, mips)
|
|
$ getopts abc optc -a -b -c
|
|
$ getopts abc optc -a -b -c
|
|
$ getopts abc optc -a
|
|
Memory fault (core dumped)
|
|
- (osf/1) POSIX says OPTIND shall be initialized to 1
|
|
$ echo $OPTIND
|
|
0
|
|
$
|
|
- (osf/1 + others?)
|
|
$ typeset -ri r=10
|
|
$ let r=12
|
|
$ echo $r
|
|
12
|
|
$
|
|
- (osf/1 + others?)
|
|
$ typeset -i a
|
|
$ typeset -L3 a
|
|
Memory fault (core dumped)
|
|
- (osf/1 + others?): -L strips leading \ \t\n\r, -R only strips trailing
|
|
spaces
|
|
$ typeset -L3 x
|
|
$ x=' ^I^J^M 2'
|
|
$ echo "($x)"
|
|
(2 )
|
|
$ typeset -R3 y
|
|
$ x='2^I^J^M '
|
|
$ echo "($x)"
|
|
(^I^J^M)
|
|
$
|
|
- (osf/1 + others?)
|
|
$ typeset +i RANDOM
|
|
Memory fault (core dumped)
|
|
- (osf/1 + others?): -L/-R/-Z clear -l/-u after assignment and vise versa
|
|
$ typeset -u x=ab
|
|
$ echo "($x)"
|
|
(AB)
|
|
$ typeset -L4 x=def
|
|
$ echo "($x)"
|
|
(DEF )
|
|
$ typeset | grep ' x$'
|
|
leftjust 4 x
|
|
$
|
|
$ typeset -L4 x=def
|
|
$ echo "($x)"
|
|
(def )
|
|
$ typeset -u x=ab
|
|
$ echo "($x)"
|
|
(AB )
|
|
$ typeset | grep ' x$'
|
|
uppercase x
|
|
$
|
|
$ typeset -i x
|
|
$ x='2()'
|
|
$ x='()'
|
|
$ x='2(4)'
|
|
- (osf/1, others?)
|
|
$ unset foo
|
|
$ echo "${foo:-"*"}"
|
|
<results of * expansion>
|
|
$
|
|
- (osf/1, others?)
|
|
$ alias blah
|
|
blah: alias not found
|
|
$ alias -x blah | grep blah
|
|
blah
|
|
$ type blah
|
|
Memory fault (core dumped)
|
|
- (osf/1, others?)
|
|
$ trap 'echo hi; false' ERR
|
|
$ false
|
|
hi
|
|
hi
|
|
....
|
|
Memory fault (core dumped)
|
|
- (osf/1, others?)
|
|
$ typeset +i ERRNO
|
|
Memory fault (core dumped)
|
|
- (osf/1, others?)
|
|
$ X=abcdef
|
|
$ echo ${X#a{b,c}e} # does not match {} inside word part of ${..#..}
|
|
abcdefe}
|
|
$
|
|
- (osf/1, others?)
|
|
$ x=f=abcdef
|
|
$ echo ${f#a|abc}
|
|
def
|
|
$ echo ${f#abc|a}
|
|
bcdef
|
|
$ echo ${f#abc|a|d}
|
|
abcdef
|
|
$
|
|
- (osf/1, hp-ux, others?)
|
|
$ i() echo hi
|
|
$ typeset -f
|
|
function i
|
|
{
|
|
hi
|
|
$
|
|
- (osf/1, others?)
|
|
$ function X {
|
|
echo start of X
|
|
function Y {
|
|
echo in Y
|
|
}
|
|
echo end of X
|
|
}
|
|
$ X
|
|
start of X
|
|
end of X
|
|
$ typeset -f
|
|
function X
|
|
{
|
|
echo start of X
|
|
function Y {
|
|
echo in Y
|
|
}
|
|
echo end of X
|
|
}
|
|
function Y
|
|
{
|
|
echo in Y
|
|
echo end of X
|
|
}
|
|
}
|
|
$
|
|
- (osf/1, others?)
|
|
$ while read x; do print -r "A $x"; done |&
|
|
[1] 18212
|
|
$ exec 8<&p
|
|
$ kill %1
|
|
Memory fault
|
|
- (osf/1, others?) Error only happens for builtin commands (/bin/echo works)
|
|
$ while read x; do print -r "A $x"; done |&
|
|
[1] 18212
|
|
$ echo hi <&p
|
|
hi
|
|
$ echo hi <&p
|
|
ksh: p: bad file unit number
|
|
$ while read x; do print -r "A $x"; done |&
|
|
ksh: process already exists
|
|
$
|
|
- (osf/1, others?) in restricted shells, command -p should not work.
|
|
$ PATH=/tmp ksh -r
|
|
$ print hi | command -p cat -n
|
|
1 hi
|
|
$
|
|
- (osf/1, others?) error message wrong for autoload files that don't define
|
|
functions
|
|
$ FPATH=/tmp
|
|
$ echo echo hi there > /tmp/aja
|
|
$ aja
|
|
hi there
|
|
ksh: echo: not found
|
|
$
|
|
- (SunOS M-12/28/93d):
|
|
$ cat -n << X $(
|
|
> echo foo
|
|
> )
|
|
> X
|
|
> echo bar
|
|
)
|
|
./ksh93: X: cannot open [No such file or directory]
|
|
Memory fault (core dumped)
|
|
|
|
POSIX sh questions (references are to POSIX 1003.2-1992)
|
|
- arithmetic expressions: how are empty expressions treated?
|
|
(eg, echo $(( ))). at&t ksh (and now pdksh) echo 0.
|
|
Same question goes for `test "" -eq 0' - does this generate an error
|
|
or, if not, what is the exit code?
|
|
- should tilde expansion occur after :'s in the word part of ${..=..}?
|
|
(me thinks it should)
|
|
- if a signal is received during the execution of a built-in,
|
|
does the builtin command exit or the whole shell?
|
|
- is it legal to execute last command of pipeline in current
|
|
execution environment (eg, can "echo foo | read bar" set
|
|
bar?)
|
|
- what action should be taken if there is an error doing a dup due
|
|
to system limits (eg, not enough feil destriptors): is this
|
|
a "redirection error" (in which case a script will exit iff the
|
|
error occured while executing a special built-in)?
|
|
IMHO, shell should exit script. Couldn't find a blanket statement
|
|
like "if shell encounters an unexpected system error, it shall
|
|
exit non-interactive scripts"...
|
|
|
|
POSIX sh bugs (references are to POSIX 1003.2-1992)
|
|
- in vi insert mode, ^W deletes to beginning of line or to the first
|
|
blank/punct character (para at line 9124, section 3). This means
|
|
"foo ^W" will do nothing. This is inconsistent with the vi
|
|
spec, which says delete preceding word including and interceding
|
|
blanks (para at line 5189, section 5).
|
|
- parameter expansion, section 3.6.2, line 391: `in each case that a
|
|
value of word is needed (..), word shall be subjected to tilde
|
|
expansion, parameter expansion, ...'. Various expansions should not
|
|
be performed if parameter is in double quotes.
|
|
- the getopts description says assigning OPTIND a value other than 1
|
|
produces undefined results, while the rationale for getopts suggests
|
|
saving/restoring the OPTIND value inside functions (since POSIX
|
|
functions don't do the save/restore automatically). Restoring
|
|
OPTIND is kind of dumb since getopts may have been in the middle
|
|
of parsing a group of flags (eg, -abc).
|
|
- unclear whether arithmetic expressions (eg, $((..))) should
|
|
understand C integer constants (ie, 0x123, 0177). at&t ksh doesn't
|
|
and neither does pdksh.
|
|
- `...` definition (3.6.3) says nothing about backslash followed by
|
|
a newline, which sh and at&t ksh strip out completely. e.g.,
|
|
$ show-args `echo 'X
|
|
Y'`
|
|
Number of args: 1
|
|
1: <XY>
|
|
$
|
|
POSIX would indicate the backslash-newline would be preserved.
|
|
- does not say how "cat << ''" is to be treated (illegal, read 'til
|
|
blank line, or read 'til eof). at&t ksh reads til eof, bourne shell
|
|
reads 'til blank line. pdksh reads 'til blank line.
|