743 lines
16 KiB
YAML
743 lines
16 KiB
YAML
name: "Here docs/strings"
|
|
cases:
|
|
- name: "Basic here doc"
|
|
stdin: |
|
|
cat <<END-MARKER
|
|
Something here...
|
|
...and here.
|
|
END-MARKER
|
|
echo "This is after"
|
|
|
|
- name: "Basic here doc in a script"
|
|
test_files:
|
|
- path: "script.sh"
|
|
contents: |
|
|
cat <<END-MARKER
|
|
Something here...
|
|
...and here.
|
|
END-MARKER
|
|
echo "This is after"
|
|
args: ["./script.sh"]
|
|
|
|
- name: "Here doc in a function"
|
|
stdin: |
|
|
function myfunc() {
|
|
if true
|
|
then
|
|
cat << END-MARKER
|
|
Something here...
|
|
END-MARKER
|
|
fi
|
|
}
|
|
|
|
myfunc
|
|
|
|
- name: "Here doc with expansions"
|
|
stdin: |
|
|
cat <<END-MARKER
|
|
Something here...
|
|
...and here.
|
|
$(echo "This is after")
|
|
END-MARKER
|
|
|
|
- name: "Here doc with expansions but quoted tag"
|
|
stdin: |
|
|
cat <<"END-MARKER"
|
|
Something here...
|
|
...and here.
|
|
$(echo "This is after")
|
|
END-MARKER
|
|
|
|
- name: "Here doc with tab removal"
|
|
stdin: |
|
|
cat <<-END-MARKER
|
|
Something here...
|
|
...and here.
|
|
END-MARKER
|
|
|
|
- name: "Basic here string"
|
|
stdin: |
|
|
shopt -ou posix
|
|
cat <<<"Something here."
|
|
wc -l <<<"Something"
|
|
|
|
- name: "Empty here doc"
|
|
stdin: |
|
|
wc -l <<EOF
|
|
EOF
|
|
|
|
- name: "Single-line here doc"
|
|
stdin: "wc -l <<EOF\nSomething\nEOF"
|
|
|
|
- name: "Here doc with other tokens after tag"
|
|
stdin: |
|
|
wc -l <<EOF | wc -l
|
|
A B C
|
|
1 2 3
|
|
EOF
|
|
|
|
- name: "Multiple here docs"
|
|
stdin: |
|
|
cat <<EOF1 <<EOF2
|
|
A B C
|
|
EOF1
|
|
1 2 3
|
|
EOF2
|
|
|
|
- name: "Here doc in a command substitution"
|
|
stdin: |
|
|
test1=$(cat <<EOF
|
|
1
|
|
2 3
|
|
4 5 6
|
|
EOF
|
|
)
|
|
|
|
echo "${test1}"
|
|
|
|
- name: "Here doc in a double-quoted command substitution"
|
|
stdin: |
|
|
test1="$(cat <<EOF
|
|
something
|
|
EOF
|
|
)"
|
|
|
|
echo "${test1}"
|
|
|
|
- name: "Here doc in a double-quoted command substitution with spaces"
|
|
stdin: |
|
|
test1="$(cat << EOF
|
|
something
|
|
EOF
|
|
)"
|
|
|
|
echo "${test1}"
|
|
|
|
- name: "Here doc in a command substitution with quotes"
|
|
stdin: |
|
|
test1=$(cat <<EOF
|
|
something "quoted"
|
|
EOF
|
|
)
|
|
|
|
echo "${test1}"
|
|
|
|
- name: "Here doc with parameter default containing quotes"
|
|
stdin: |
|
|
cat <<EOF
|
|
${unset_var:-"default value"}
|
|
EOF
|
|
|
|
- name: "Here doc preserves literal double quotes"
|
|
stdin: |
|
|
cat <<EOF
|
|
"double quoted"
|
|
she said "hello"
|
|
EOF
|
|
|
|
- name: "Here doc preserves literal single quotes"
|
|
stdin: |
|
|
cat <<EOF
|
|
'single quoted'
|
|
it's a test
|
|
EOF
|
|
|
|
- name: "Here doc with mixed quotes and expansion"
|
|
stdin: |
|
|
x=world
|
|
cat <<EOF
|
|
"hello $x"
|
|
"$x" and more
|
|
EOF
|
|
|
|
- name: "Here doc does not perform brace expansion"
|
|
stdin: |
|
|
cat <<EOF
|
|
{a,b,c}
|
|
{1..3}
|
|
EOF
|
|
|
|
- name: "Here doc does not perform tilde expansion"
|
|
stdin: |
|
|
cat <<EOF
|
|
~
|
|
~/path
|
|
EOF
|
|
|
|
- name: "Here doc escape sequences"
|
|
stdin: |
|
|
cat <<EOF
|
|
\$HOME
|
|
\\backslash
|
|
\n not a newline
|
|
\"not unquoted\"
|
|
\'not unquoted\'
|
|
EOF
|
|
|
|
- name: "Here doc line continuation"
|
|
stdin: |
|
|
cat <<EOF
|
|
alpha\
|
|
beta
|
|
double\\
|
|
backslash
|
|
$(echo 'single\
|
|
quotes') `echo back\
|
|
quotes`
|
|
EOF
|
|
cat <<'EOF'
|
|
alpha\
|
|
beta
|
|
EOF
|
|
cat <<\EOF
|
|
alpha\
|
|
beta $HOME
|
|
EOF
|
|
cat <<'EOF'
|
|
\
|
|
quoted body starting with a line continuation
|
|
EOF
|
|
|
|
- name: "Here doc backquote escape rules"
|
|
stdin: |
|
|
cat <<EOF
|
|
`echo \"q\"`
|
|
`echo \\\\z`
|
|
EOF
|
|
|
|
- name: "Here doc end tag joined by a line continuation"
|
|
stdin: |
|
|
cat <<EOF
|
|
body
|
|
E\
|
|
OF
|
|
echo after
|
|
cat <<EOF
|
|
body\
|
|
EOF
|
|
EOF
|
|
|
|
- name: "Here doc end tag line continued onto the next line"
|
|
stdin: |
|
|
cat <<E
|
|
abc
|
|
E\
|
|
X
|
|
E
|
|
echo done
|
|
|
|
- name: "Here doc line continuation with tab stripping"
|
|
stdin: "cat <<-EOF\n\talpha\\\n\tbeta\n\tEOF\n"
|
|
|
|
- name: "Here doc with command substitution containing quotes"
|
|
stdin: |
|
|
cat <<EOF
|
|
$(echo "hello world")
|
|
$(echo 'single')
|
|
EOF
|
|
|
|
- name: "Here doc with arithmetic expansion"
|
|
stdin: |
|
|
cat <<EOF
|
|
$((2 + 3))
|
|
$((10 * 2))
|
|
EOF
|
|
|
|
- name: "Here doc in a command substitution with parentheses"
|
|
stdin: |
|
|
test1=$(cat <<EOF
|
|
(something)
|
|
EOF
|
|
)
|
|
|
|
echo "${test1}"
|
|
|
|
- name: "Here doc in a command substitution inside arithmetic"
|
|
stdin: |
|
|
echo $(( $(cat <<EOF
|
|
1
|
|
EOF
|
|
) + 2 ))
|
|
|
|
- name: "Here doc body closing paren in a command substitution inside arithmetic"
|
|
known_failure: true # TODO(tokenizer): bash runs the body's `)` as a command, expanding `$((...))` to nothing; brush reads the body and fails to evaluate `) + 2`
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo $(( $(cat <<EOF
|
|
)
|
|
EOF
|
|
) + 2 ))
|
|
echo "rc=$?"
|
|
|
|
- name: "Here doc with parentheses"
|
|
stdin: |
|
|
cat <<EOF
|
|
(something)
|
|
EOF
|
|
|
|
- name: "Multiple here docs in a command substitution"
|
|
stdin: |
|
|
test1=$(cat <<EOF1 <<EOF2
|
|
A B C
|
|
EOF1
|
|
D E F
|
|
EOF2
|
|
)
|
|
|
|
echo "${test1}"
|
|
|
|
- name: "Complex here docs in a command substitution"
|
|
stdin: |
|
|
test1=$(cat <<EOF1 <<EOF2 | wc -l
|
|
A B C
|
|
EOF1
|
|
D E F
|
|
EOF2
|
|
)
|
|
|
|
echo "${test1}"
|
|
|
|
- name: "Here doc with quoted delimiter"
|
|
stdin: |
|
|
cat <<'EOF'
|
|
something
|
|
EOF
|
|
|
|
- name: "Here doc with quoted delimiter containing backslashes and quotes"
|
|
stdin: |
|
|
cat <<'a\b'
|
|
single
|
|
a\b
|
|
cat <<"a\b"
|
|
double
|
|
a\b
|
|
cat <<"x'y"
|
|
mixed
|
|
x'y
|
|
cat <<a\'b
|
|
escaped
|
|
a'b
|
|
|
|
- name: "Here doc with double quoted delimiter"
|
|
stdin: |
|
|
cat <<"EOF"
|
|
something
|
|
EOF
|
|
|
|
- name: "Here doc with quoted delimiter inside command substitution"
|
|
stdin: |
|
|
$(cat <<'EOF'
|
|
true
|
|
EOF
|
|
)
|
|
|
|
- name: "Here doc with double quoted delimiter inside command substitution"
|
|
stdin: |
|
|
$(cat <<"EOF"
|
|
true
|
|
EOF
|
|
)
|
|
|
|
- name: "Here doc with unpaired single quote in command substitution"
|
|
stdin: |
|
|
echo "$(cat <<'EOF'
|
|
it's here
|
|
EOF
|
|
)"
|
|
|
|
- name: "Here doc with unpaired backtick in command substitution"
|
|
stdin: |
|
|
echo "$(cat <<'EOF'
|
|
a ` b
|
|
EOF
|
|
)"
|
|
|
|
- name: "Here doc with three single quotes in command substitution"
|
|
stdin: |
|
|
echo "$(cat <<'EOF'
|
|
it's Bob's it's
|
|
EOF
|
|
)"
|
|
|
|
- name: "Here doc with unpaired single quote in command substitution (#420)"
|
|
stdin: |
|
|
var=$(
|
|
sed 's/nice/cool/' - <<-EOF
|
|
that's
|
|
"nice"
|
|
EOF
|
|
)
|
|
echo $var
|
|
|
|
- name: "Here doc with unpaired double quote in command substitution (#1066)"
|
|
stdin: |
|
|
echo "$(cat <<'EOF'
|
|
she said "hello
|
|
EOF
|
|
)"
|
|
|
|
- name: "Here doc with all three unmatched delimiters in command substitution (#1066)"
|
|
stdin: |
|
|
echo "$(cat <<'EOF'
|
|
`hello "world it's
|
|
EOF
|
|
)"
|
|
|
|
- name: "Here doc with quoted parens and backticks in command substitution (#1066)"
|
|
stdin: |
|
|
printf '%s\n' "$(cat <<'EOF'
|
|
a (b's c) `touch pwned` d's
|
|
EOF
|
|
)"
|
|
|
|
- name: "Here doc with unpaired close paren in command substitution"
|
|
stdin: |
|
|
echo "$(cat <<END
|
|
This one: “)”
|
|
END
|
|
)"
|
|
|
|
- name: "Here doc body starting with close paren in command substitution"
|
|
stdin: |
|
|
x=$(cat <<END
|
|
)
|
|
END
|
|
)
|
|
echo "x=$x"
|
|
echo $(cat <<END
|
|
) two
|
|
END
|
|
)
|
|
cat <<OUTER
|
|
$(cat <<END
|
|
) three
|
|
END
|
|
)
|
|
OUTER
|
|
|
|
- name: "Here doc in command substitution in parameter default"
|
|
stdin: |
|
|
echo "${unset_var:-$(cat <<'EOF'
|
|
she said "hello ) it's
|
|
EOF
|
|
)}"
|
|
|
|
- name: "Multiple here docs with unbalanced quotes in command substitution"
|
|
stdin: |
|
|
echo "$(cat <<A <<'B'
|
|
a "1
|
|
A
|
|
b )2 it's
|
|
B
|
|
)"
|
|
|
|
- name: "Tab-stripped here doc with unbalanced quotes in command substitution"
|
|
stdin: |
|
|
echo "$(cat <<-'EOF'
|
|
tab "stripped )
|
|
EOF
|
|
)"
|
|
|
|
- name: "Here doc body containing command substitution with its own here doc"
|
|
stdin: |
|
|
cat <<EOF
|
|
body $(cat <<'INNER'
|
|
inner "quote ) it's
|
|
INNER
|
|
) end
|
|
EOF
|
|
|
|
- name: "Eval of here doc with unbalanced quotes in command substitution"
|
|
stdin: |
|
|
eval 'echo "$(cat <<EOF
|
|
ev "al )
|
|
EOF
|
|
)"'
|
|
|
|
- name: "Here doc with unterminated command substitution"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
cat <<EOF
|
|
a $(echo hi b
|
|
EOF
|
|
echo "rc=$?"
|
|
|
|
- name: "Here doc with unterminated parameter expansion"
|
|
known_failure: true # TODO(expansion): #540: unterminated '${' is silently treated as literal text
|
|
ignore_stderr: true
|
|
stdin: |
|
|
cat <<EOF
|
|
a ${USER b
|
|
EOF
|
|
echo "rc=$?"
|
|
|
|
- name: "Here doc with command substitution that never closes"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
cat <<EOF
|
|
a $(echo 'b) c
|
|
EOF
|
|
echo "rc=$?"
|
|
cat <<EOF
|
|
a $(echo b # c)
|
|
EOF
|
|
echo "rc=$?"
|
|
|
|
- name: "Here doc with unterminated arithmetic expansions"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
cat <<EOF
|
|
a $((1 + b
|
|
EOF
|
|
echo "rc=$?"
|
|
cat <<EOF
|
|
a $[1 + b
|
|
EOF
|
|
echo "rc=$?"
|
|
|
|
- name: "Here doc tag containing command substitution"
|
|
stdin: |
|
|
cat <<$(x)
|
|
body
|
|
$(x)
|
|
|
|
- name: "Here doc tag containing parameter expansion"
|
|
stdin: |
|
|
cat <<E${x}
|
|
body
|
|
E${x}
|
|
|
|
- name: "Here doc tag containing arithmetic expansion"
|
|
stdin: |
|
|
cat <<$((1 + 2))
|
|
body
|
|
$((1 + 2))
|
|
cat <<$[1]
|
|
body
|
|
$[1]
|
|
|
|
- name: "Expansions after here doc tag on the same line"
|
|
stdin: |
|
|
printf '[%s]\n' <<EOF $(echo a b) "$((1 + 2))" "${x:-y}" $[4]
|
|
ignored
|
|
EOF
|
|
|
|
- name: "Here doc in command substitution with multiple blanks before tag"
|
|
stdin: |
|
|
echo "$(cat << EOF
|
|
two blanks
|
|
EOF
|
|
)"
|
|
echo "$(cat <<- EOF
|
|
two tabs
|
|
EOF
|
|
)"
|
|
|
|
- name: "Here doc in command substitution on the same line as another here doc"
|
|
stdin: |
|
|
cat <<A; echo "$(cat <<B
|
|
inner body
|
|
B
|
|
)"
|
|
outer body
|
|
A
|
|
|
|
- name: "Here doc started in command substitution with body after it"
|
|
ignore_stderr: true # bash warns about the unterminated here-document
|
|
stdin: |
|
|
tr a-z A-Z <<<"[$(cat <<EOF)]"
|
|
body
|
|
EOF
|
|
echo after
|
|
|
|
- name: "Here doc started in command substitution while another here doc is pending"
|
|
ignore_stderr: true # bash warns about the unterminated here-document
|
|
stdin: |
|
|
cat <<A; echo "[$(cat <<B)]"
|
|
b body
|
|
B
|
|
a body
|
|
A
|
|
echo "[$(cat <<C)]" "[$(cat <<D)]"
|
|
c body
|
|
C
|
|
d body
|
|
D
|
|
|
|
- name: "Here doc started in nested command substitution with body after it"
|
|
ignore_stderr: true # bash warns about the unterminated here-document
|
|
stdin: |
|
|
echo $(echo $(cat <<EOF)
|
|
body
|
|
EOF
|
|
echo more
|
|
)
|
|
|
|
- name: "Here doc started in doubly nested command substitution with body after it"
|
|
ignore_stderr: true # bash warns about the unterminated here-document
|
|
stdin: |
|
|
echo "[$(echo $(cat <<EOF))]"
|
|
body
|
|
EOF
|
|
|
|
- name: "Here doc started in command substitution in parameter expansion with body after it"
|
|
ignore_stderr: true # bash warns about the unterminated here-document
|
|
stdin: |
|
|
echo "[${x:-$(cat <<EOF)}]"
|
|
body
|
|
EOF
|
|
|
|
- name: "Here doc started in command substitution with commands after it and body after it"
|
|
ignore_stderr: true # bash warns about the unterminated here-documents
|
|
stdin: |
|
|
echo "[$(cat <<EOF | tr a-z A-Z)]"
|
|
body
|
|
EOF
|
|
echo "[$(cat <<A; cat <<-B)]"
|
|
a
|
|
A
|
|
b
|
|
B
|
|
|
|
- name: "Here doc started in command substitution in command with its own here doc"
|
|
ignore_stderr: true # bash warns about the unterminated here-document
|
|
stdin: |
|
|
f() { echo "arg=$1"; cat; }
|
|
f "[$(cat <<B)]" <<A
|
|
b
|
|
B
|
|
a
|
|
A
|
|
|
|
- name: "Here doc started in command substitution with line continuation after it"
|
|
known_failure: true # TODO(input): reading a line at a time, the check for a trailing line continuation misses one set aside until after the body, so the command runs without the line it continues onto
|
|
ignore_stderr: true # bash warns about the unterminated here-document
|
|
stdin: |
|
|
echo "[$(cat <<EOF)]" \
|
|
more
|
|
body
|
|
EOF
|
|
echo after
|
|
|
|
- name: "Here doc operator with no tag in command substitution"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
echo "[$(cat <<)]"
|
|
echo after
|
|
|
|
- name: "Here doc operator inside parameter expansion is literal"
|
|
stdin: |
|
|
echo ${x:-<<}
|
|
echo ${x:-<< foo}
|
|
echo "${x:-a<<b}"
|
|
echo ${x:-<<-EOF}
|
|
echo after
|
|
|
|
- name: "Blanks and operators inside parameter expansion word"
|
|
stdin: |
|
|
echo "[${x:- | }]" "[${x:-a b}]" "[${x:-a;b}]" "[${x:- #y}]" "[${x:-a #b}]"
|
|
echo "[${x:-(a) && b}]"
|
|
|
|
- name: "Shift operator after nested arithmetic expansion"
|
|
stdin: |
|
|
echo $(( $((1)) << 2 ))
|
|
echo $[ $((1)) << 3 ]
|
|
(( x = $((1)) << 4 )); echo "$x"
|
|
|
|
- name: "Here doc after arithmetic command in command substitution"
|
|
stdin: |
|
|
x=$( ((1)) ); cat <<EOF
|
|
body
|
|
EOF
|
|
|
|
- name: "Here doc after arithmetic command inside command substitution"
|
|
stdin: |
|
|
x=$( ((1)); cat <<EOF
|
|
a)b
|
|
EOF
|
|
)
|
|
echo "[$x]"
|
|
|
|
- name: "Shift operator after parenthesized arithmetic in arithmetic expansion"
|
|
stdin: |
|
|
echo $(( ((1)) << 2 )) $[ ((1)) << 3 ]
|
|
|
|
- name: "Here doc in subshell in command substitution"
|
|
stdin: |
|
|
x=$( (cat <<EOF; echo x) | tr a-z A-Z
|
|
body
|
|
EOF
|
|
)
|
|
echo "[$x]"
|
|
|
|
- name: "Here doc pending while a later expansion spans lines"
|
|
stdin: |
|
|
cat <<A; echo "$(echo x
|
|
echo y)"
|
|
a
|
|
A
|
|
cat <<B; echo "${x:-p
|
|
q}"
|
|
b
|
|
B
|
|
cat <<C; echo $[ 1 +
|
|
2 ]
|
|
c
|
|
C
|
|
|
|
- name: "Here doc body with command substitution that leaves a here doc open"
|
|
ignore_stderr: true # bash warns about the unterminated here-documents
|
|
stdin: |
|
|
cat <<X
|
|
a $(cat <<EOF) b
|
|
X
|
|
echo "rc=$?"
|
|
|
|
- name: "Here doc body with command substitution that doesn't parse"
|
|
known_failure: true # TODO(expansion): bash fails the command when a substitution in the body doesn't parse; brush runs it with the substitution empty
|
|
ignore_stderr: true
|
|
stdin: |
|
|
cat <<X
|
|
a $(if) b
|
|
X
|
|
echo "rc=$?"
|
|
|
|
- name: "Here doc tag quote removal with nested quotes and backslashes"
|
|
ignore_stderr: true
|
|
stdin: |
|
|
cat <<"'"
|
|
a
|
|
'
|
|
cat <<'"x"'
|
|
b
|
|
"x"
|
|
cat <<'a\b'
|
|
c
|
|
a\b
|
|
echo after
|
|
|
|
- name: "Here doc tag with ANSI-C quoting"
|
|
known_failure: true # TODO(tokenizer): $'...' in a here tag isn't decoded, so the tag is never matched
|
|
ignore_stderr: true
|
|
stdin: |
|
|
cat <<$'E\x4fF'
|
|
a
|
|
EOF
|
|
echo after
|
|
|
|
- name: "Here doc delimited by end of input"
|
|
known_failure: true # TODO(tokenizer): bash warns and ends the body at the end of the input; brush rejects the script
|
|
ignore_stderr: true
|
|
stdin: |
|
|
cat <<EOF
|
|
body
|
|
|
|
- name: "Here doc in command substitution closed on its tag line"
|
|
known_failure: true # TODO(tokenizer): bash ends the body at a line holding the tag followed by the substitution's ')'
|
|
ignore_stderr: true
|
|
stdin: |
|
|
x=$(cat <<EOF
|
|
body
|
|
EOF)
|
|
echo "[$x]"
|