Skip to content

Commit 5d56b35

Browse files
committed
Redo trailing \ handling, overhaul HERE document plumbing,
handle undelimited redirects (I.E. cat<file without spaces), remove unused third argument to parse_word() Note: get_next_line() now returns trailing \n so we can distinguish lines that end with \n from undelimited last line of input. And parse_line() now receives NULL to flush pending line continuations (HERE documents give a warning, logic continuations should syntax error.
1 parent 87abbca commit 5d56b35

File tree

2 files changed

+102
-74
lines changed

2 files changed

+102
-74
lines changed

tests/sh.test

Lines changed: 55 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,20 @@
11
#!/bin/echo no
22

3-
# TODO: categorize tests
4-
5-
# TODO https://mywiki.wooledge.org/BashFAQ
6-
# http://tiswww.case.edu/php/chet/bash/FAQ
7-
# https://mywiki.wooledge.org/BashPitfalls#set_-euo_pipefail
8-
9-
# // ${#} ${#x} ${#@} ${#x[@]} ${#!} ${!#}
10-
# // ${!} ${!@} ${!@Q} ${!x} ${!x@} ${!x@Q} ${!x#} ${!x[} ${!x[*]}
11-
12-
# Looked like a prefix but wasn't: three chars (@ # -) are both paremeter name
13-
# and slice operator. When immediately followed by } it's parameter, otherwise
14-
# we did NOT have a prefix and it's an operator.
15-
#
16-
# ${#-} ${#-abc}
17-
# ${##} ${##0}
18-
# ${#@} ${#@Q}
19-
#
20-
# backslash not discarded: echo "abc\"def"
21-
22-
# ${x:-y} use default
23-
# ${x:=y} assign default (error if positional)
24-
# ${x:?y} err if null
25-
# ${x:+y} alt value
26-
# ${x:off} ${x:off:len} off<0 from end (must ": -"), len<0 also from end must
27-
# 0-based indexing
28-
# ${@:off:len} positional parameters, off -1 = len, -len is error
29-
# 1-based indexing
30-
31-
# [] wins over +()
32-
# touch 'AB[DEF]'; echo AB[+(DEF]) AB[+(DEF)?
33-
# AB[+(DEF]) AB[DEF]
34-
35-
# Testing shell corner cases _within_ a shell script is kind of hard.
36-
373
[ -f testing.sh ] && . testing.sh
384

395
# TODO make fake pty wrapper for test infrastructure
406

41-
#testing "name" "command" "result" "infile" "stdin"
7+
# testing "name" "command" "result" "infile" "stdin"
8+
# texpect "name" "command" [R]I/O/E"string" X[ERR]
429

43-
# texpect "name" "command" E/O/I"string"
44-
45-
# Use "bash" name for host, "sh" for toybox
10+
# Use "bash" name for host, "sh" for toybox. (/bin/sh is often defective.)
4611
[ -z "$SH" ] && { [ -z "$TEST_HOST" ] && SH="sh" || export SH="bash" ; }
47-
# Prompt changes for root/normal user
48-
[ $(id -u) -eq 0 ] && P='# ' || P='$ '
49-
# run sufficiently isolated shell child process to get predictable results
12+
# The expected prompt is different for root vs normal user
13+
[ $UID -eq 0 ] && P='# ' || P='$ '
14+
# insulate shell child process to get predictable results
5015
SS="env -i PATH=${PATH@Q} PS1='\\$ ' $SH --noediting --noprofile --norc -is"
5116

17+
# Wrap txpect for shell testing
5218
shxpect() {
5319
X="$1"
5420
shift
@@ -57,10 +23,11 @@ shxpect() {
5723

5824
shxpect "prompt and exit" I$'exit\n'
5925
shxpect "prompt and echo" I$'echo hello\n' O$'hello\n' E"$P"
26+
shxpect "redir" I$'cat<<<hello\n' O$'hello\n' E"$P"
6027
shxpect "redirect err" I$'echo > /dev/full\n' E E"$P" X1
6128
shxpect "wait for <(exit)" I$'cat <(echo hello 1>&2)\n' E$'hello\n' E"$P"
6229

63-
# Test the sh -c stuff before changing EVAL
30+
# Test shell command line (-c and how scripts run) before changing EVAL
6431
testing '-c "" exit status 0' '$SH -c "" && echo $?' '0\n' '' ''
6532
testing '-c args' "\$SH -c 'echo \$0,\$1,\$2,\$3' one two three four five" \
6633
"one,two,three,four\n" "" ""
@@ -72,6 +39,12 @@ testing '-c arg split2' \
7239
"$SH -c 'for i in a\"\$* \$@\"b; do echo =\$i=;done' one two three four five"\
7340
"=atwo three four five two=\n=three=\n=four=\n=fiveb=\n" "" ""
7441
testing '-c arg count' "$SH -c 'echo \$#' 9 8 7 6 1 2 3 4" "7\n" "" ""
42+
testing 'trailing \' "$SH -c 'echo \'" '\\\n' '' ''
43+
testing "trailing \\ in ''" "$SH -c \$'echo \\'one\\\\\\ntwo\\''" \
44+
'one\\\ntwo\n' '' ''
45+
testing 'trailing \ in ""' "$SH -c \$'echo \"one\\\\\\ntwo\"'" 'onetwo\n' \
46+
'' ''
47+
testing 'vanishing \' "$SH -c \$'echo \\\\\\n a'" 'a\n' '' ''
7548
testing "exec3" '$C -c "{ exec readlink /proc/self/fd/0;} < /proc/self/exe"' \
7649
"$(readlink -f $C)\n" "" ""
7750
testing 'arg shift' "$SH -c '"'for i in "" 2 1 1 1; do echo $? $1; shift $i; done'"' one two three four five" \
@@ -121,6 +94,8 @@ testing "eval0" "$SH -c 'eval echo \$*' one two three" "two three\n" "" ""
12194
# Change EVAL to call sh -c for us, using "bash" explicitly for the host.
12295
export EVAL="timeout 10 $SH -c"
12396

97+
# From here on, tests run within the new shell by default.
98+
12499
testing 'return code' 'if false; then echo true; fi; echo $?' '0\n' '' ''
125100
testing 'return code 2' 'if true; then false; fi; echo $?' '1\n' '' ''
126101
testing 'return code 3' 'x=0; while [ $((x++)) -lt 2 ]; do echo $x; done; echo $?' '1\n2\n0\n' '' ''
@@ -130,8 +105,12 @@ testing 'local var +whiteout' \
130105
testing 'escape passthrough' 'echo -e "a\nb\nc\td"' 'a\nb\nc\td\n' '' ''
131106

132107
testing 'trailing $ is literal' 'echo $' '$\n' '' ''
108+
testing 'work after HERE' $'cat<<0;echo hello\npotato\n0' 'potato\nhello\n' '' ''
109+
testing '<<""' $'cat<<"";echo hello\npotato\n\necho huh' 'potato\nhello\nhuh\n'\
110+
'' ''
133111
# TODO testing 'empty +() is literal' 'echo +()' '+()\n' '' ''
134112

113+
# shxpect "EOF" I$'<<EOF;echo hello'
135114
shxpect 'queued work after HERE' I$'<<0;echo hello\n' E"> " I$'0\n' O$'hello\n'
136115
shxpect '$_ preserved on assignment error' I$'true hello; a=1 b=2 c=${}\n' \
137116
E E"$P" I$'echo $_\n' O$'hello\n'
@@ -471,6 +450,7 @@ testing 'sequence check' 'IFS=x; X=abxcd; echo ${X/bxc/g}' 'agd\n' '' ''
471450

472451
shxpect '${ with newline' I$'HELLO=abc; echo ${HELLO/b/\n' E"> " I$'}\n' O$'a c\n'
473452

453+
testing 'here0' 'cat<<<hello' 'hello\n' '' ''
474454
shxpect 'here1' I$'POTATO=123; cat << EOF\n' E"> " \
475455
I$'$POTATO\n' E"> " I$'EOF\n' O$'123\n'
476456
shxpect 'here2' I$'POTATO=123; cat << E"O"F\n' E"> " \
@@ -739,3 +719,36 @@ testing '[[~]]' '[[ ~ == $HOME ]] && echo yes' 'yes\n' '' ''
739719
#+ *) SKIPNEXT=1 ;;
740720
#+ esac
741721

722+
# TODO: categorize tests
723+
724+
# TODO https://mywiki.wooledge.org/BashFAQ
725+
# http://tiswww.case.edu/php/chet/bash/FAQ
726+
# https://mywiki.wooledge.org/BashPitfalls#set_-euo_pipefail
727+
728+
# // ${#} ${#x} ${#@} ${#x[@]} ${#!} ${!#}
729+
# // ${!} ${!@} ${!@Q} ${!x} ${!x@} ${!x@Q} ${!x#} ${!x[} ${!x[*]}
730+
731+
# Looked like a prefix but wasn't: three chars (@ # -) are both paremeter name
732+
# and slice operator. When immediately followed by } it's parameter, otherwise
733+
# we did NOT have a prefix and it's an operator.
734+
#
735+
# ${#-} ${#-abc}
736+
# ${##} ${##0}
737+
# ${#@} ${#@Q}
738+
#
739+
# backslash not discarded: echo "abc\"def"
740+
741+
# ${x:-y} use default
742+
# ${x:=y} assign default (error if positional)
743+
# ${x:?y} err if null
744+
# ${x:+y} alt value
745+
# ${x:off} ${x:off:len} off<0 from end (must ": -"), len<0 also from end must
746+
# 0-based indexing
747+
# ${@:off:len} positional parameters, off -1 = len, -len is error
748+
# 1-based indexing
749+
750+
# [] wins over +()
751+
# touch 'AB[DEF]'; echo AB[+(DEF]) AB[+(DEF)?
752+
# AB[+(DEF]) AB[DEF]
753+
754+
# Testing shell corner cases _within_ a shell script is kind of hard.

0 commit comments

Comments
 (0)