View
@@ -1,178 +1,178 @@
#!/usr/bin/env bash
### [[ glob matching, [[ has no glob expansion
#### [[ glob matching, [[ has no glob expansion
[[ foo.py == *.py ]] && echo true
[[ foo.p == *.py ]] || echo false
# stdout-json: "true\nfalse\n"
## stdout-json: "true\nfalse\n"
### [[ glob matching with escapes
#### [[ glob matching with escapes
[[ 'foo.*' == *."*" ]] && echo true
# note that the pattern arg to fnmatch should be '*.\*'
# stdout: true
## stdout: true
### equality
#### equality
[[ '*.py' == '*.py' ]] && echo true
[[ foo.py == '*.py' ]] || echo false
# stdout-json: "true\nfalse\n"
## stdout-json: "true\nfalse\n"
### [[ glob matching with unquoted var
#### [[ glob matching with unquoted var
pat=*.py
[[ foo.py == $pat ]] && echo true
[[ foo.p == $pat ]] || echo false
# stdout-json: "true\nfalse\n"
## stdout-json: "true\nfalse\n"
### [[ regex matching
#### [[ regex matching
# mksh doesn't have this syntax of regex matching. I guess it comes from perl?
regex='.*\.py'
[[ foo.py =~ $regex ]] && echo true
[[ foo.p =~ $regex ]] || echo false
# stdout-json: "true\nfalse\n"
# N-I mksh stdout-json: ""
# N-I mksh status: 1
## stdout-json: "true\nfalse\n"
## N-I mksh stdout-json: ""
## N-I mksh status: 1
### [[ regex syntax error
#### [[ regex syntax error
# hm, it doesn't show any error, but it exits 2.
[[ foo.py =~ * ]] && echo true
# status: 2
# N-I mksh status: 1
## status: 2
## N-I mksh status: 1
### [[ has no word splitting
#### [[ has no word splitting
var='one two'
[[ 'one two' == $var ]] && echo true
# stdout: true
## stdout: true
### [[ has quote joining
#### [[ has quote joining
var='one two'
[[ 'one 'tw"o" == $var ]] && echo true
# stdout: true
## stdout: true
### [[ empty string is false
#### [[ empty string is false
[[ 'a' ]] && echo true
[[ '' ]] || echo false
# stdout-json: "true\nfalse\n"
## stdout-json: "true\nfalse\n"
### && chain
#### && chain
[[ t && t && '' ]] || echo false
# stdout: false
## stdout: false
### || chain
#### || chain
[[ '' || '' || t ]] && echo true
# stdout: true
## stdout: true
### [[ compound expressions
#### [[ compound expressions
# Notes on whitespace:
# - 1 and == need space seprating them, but ! and ( don't.
# - [[ needs whitesapce after it, but ]] doesn't need whitespace before it!
[[ ''||! (1 == 2)&&(2 == 2)]] && echo true
# stdout: true
## stdout: true
# NOTE on the two cases below. We're comparing
# (a || b) && c vs. a || (b && c)
#
# a = true, b = false, c = false is an example where they are different.
# && and || have precedence inside
### precedence of && and || inside [[
#### precedence of && and || inside [[
[[ True || '' && '' ]] && echo true
# stdout: true
## stdout: true
### precedence of && and || in a command context
#### precedence of && and || in a command context
if test True || test '' && test ''; then
echo YES
else
echo "NO precedence"
fi
# stdout: NO precedence
## stdout: NO precedence
# http://tldp.org/LDP/abs/html/testconstructs.html#DBLBRACKETS
### Octal literals with -eq
#### Octal literals with -eq
decimal=15
octal=017 # = 15 (decimal)
[[ $decimal -eq $octal ]] && echo true
[[ $decimal -eq ZZZ$octal ]] || echo false
# stdout-json: "true\nfalse\n"
# N-I mksh stdout: false
## stdout-json: "true\nfalse\n"
## N-I mksh stdout: false
# mksh doesn't implement this syntax for literals.
### Hex literals with -eq
#### Hex literals with -eq
decimal=15
hex=0x0f # = 15 (decimal)
[[ $decimal -eq $hex ]] && echo true
[[ $decimal -eq ZZZ$hex ]] || echo false
# stdout-json: "true\nfalse\n"
# N-I mksh stdout: false
## stdout-json: "true\nfalse\n"
## N-I mksh stdout: false
# TODO: Add tests for this
# https://www.gnu.org/software/bash/manual/bash.html#Bash-Conditional-Expressions
# When used with [[, the ‘<’ and ‘>’ operators sort lexicographically using the current locale. The test command uses ASCII ordering.
### > on strings
#### > on strings
# NOTE: < doesn't need space, even though == does? That's silly.
[[ b>a ]] && echo true
[[ b<a ]] || echo false
# stdout-json: "true\nfalse\n"
## stdout-json: "true\nfalse\n"
### != on strings
#### != on strings
# NOTE: b!=a does NOT work
[[ b != a ]] && echo true
[[ a != a ]] || echo false
# stdout-json: "true\nfalse\n"
## stdout-json: "true\nfalse\n"
### -eq on strings
#### -eq on strings
# This is lame behavior: it does a conversion to 0 first for any string
[[ a -eq a ]] && echo true
[[ a -eq b ]] && echo true
# stdout-json: "true\ntrue\n"
# OK bash/mksh stdout-json: "true\ntrue\n"
## stdout-json: "true\ntrue\n"
## OK bash/mksh stdout-json: "true\ntrue\n"
### [[ compare with literal -f (compare with test-builtin.test.sh)
#### [[ compare with literal -f (compare with test-builtin.test.sh)
var=-f
[[ $var == -f ]] && echo true
[[ '-f' == $var ]] && echo true
# stdout-json: "true\ntrue\n"
## stdout-json: "true\ntrue\n"
### [[ with op variable (compare with test-builtin.test.sh)
#### [[ with op variable (compare with test-builtin.test.sh)
# Parse error -- parsed BEFORE evaluation of vars
op='=='
[[ a $op a ]] && echo true
[[ a $op b ]] || echo false
# status: 2
# OK mksh status: 1
## status: 2
## OK mksh status: 1
### [[ with unquoted empty var (compare with test-builtin.test.sh)
#### [[ with unquoted empty var (compare with test-builtin.test.sh)
empty=''
[[ $empty == '' ]] && echo true
# stdout: true
## stdout: true
### [[ at runtime doesn't work
#### [[ at runtime doesn't work
dbracket=[[
$dbracket foo == foo ]]
# status: 127
## status: 127
### [[ with env prefix doesn't work
#### [[ with env prefix doesn't work
FOO=bar [[ foo == foo ]]
# status: 127
## status: 127
### [[ over multiple lines is OK
#### [[ over multiple lines is OK
# Hm it seems you can't split anywhere?
[[ foo == foo
&& bar == bar
]] && echo true
# status: 0
# stdout-json: "true\n"
## status: 0
## stdout-json: "true\n"
### Argument that looks like a command word operator
#### Argument that looks like a command word operator
[[ -f -f ]] || echo false
[[ -f == ]] || echo false
# stdout-json: "false\nfalse\n"
## stdout-json: "false\nfalse\n"
### Argument that looks like a real operator
#### Argument that looks like a real operator
[[ -f < ]]
# status: 2
# OK mksh status: 1
## status: 2
## OK mksh status: 1
### Does user array equal "$@" ?
#### Does user array equal "$@" ?
# Oh it coerces both to a string. Lame.
# I think it disobeys "${a[@]}", and treats it like an UNQUOTED ${a[@]}.
# NOTE: set -o strict-array should make this invalid
@@ -186,7 +186,7 @@ true
false
## END
### Array coerces to string
#### Array coerces to string
# NOTE: set -o strict-array should make this invalid
a=(1 3 5)
[[ '1 3 5' = "${a[@]}" ]] && echo true
@@ -196,68 +196,68 @@ true
false
## END
### Quotes don't matter in comparison
#### Quotes don't matter in comparison
[[ '3' = 3 ]] && echo true
[[ '3' -eq 3 ]] && echo true
# stdout-json: "true\ntrue\n"
## stdout-json: "true\ntrue\n"
### -eq with arithmetic expression!
#### -eq with arithmetic expression!
[[ 1+2 -eq 3 ]] && echo true
expr='1+2'
[[ $expr -eq 3 ]] && echo true # must be dynamically parsed
# stdout-json: "true\ntrue\n"
## stdout-json: "true\ntrue\n"
### -eq coercion produces weird results
#### -eq coercion produces weird results
[[ '' -eq 0 ]] && echo true
# stdout: true
## stdout: true
### [[ '(' ]] is treated as literal
#### [[ '(' ]] is treated as literal
[[ '(' ]]
echo status=$?
# stdout: status=0
## stdout: status=0
### [[ '(' foo ]] is syntax error
#### [[ '(' foo ]] is syntax error
[[ '(' foo ]]
echo status=$?
# status: 2
# OK mksh status: 1
## status: 2
## OK mksh status: 1
### empty ! is treated as literal
#### empty ! is treated as literal
[[ '!' ]]
echo status=$?
# stdout: status=0
## stdout: status=0
### [[ -z ]] is syntax error
#### [[ -z ]] is syntax error
[[ -z ]]
echo status=$?
# status: 2
# OK mksh status: 1
## status: 2
## OK mksh status: 1
### [[ -z '>' ]]
#### [[ -z '>' ]]
[[ -z '>' ]] || echo false # -z is operator
# stdout: false
## stdout: false
### [[ -z '>' a ]] is syntax error
#### [[ -z '>' a ]] is syntax error
[[ -z '>' -- ]]
echo status=$?
# status: 2
# OK mksh status: 1
## status: 2
## OK mksh status: 1
### test whether ']]' is empty
#### test whether ']]' is empty
[[ ']]' ]]
echo status=$?
# status: 0
## status: 0
### [[ ]] is syntax error
#### [[ ]] is syntax error
[[ ]]
echo status=$?
# stdout-json: ""
# status: 2
# OK mksh status: 1
## stdout-json: ""
## status: 2
## OK mksh status: 1
### [[ && ]] is syntax error
#### [[ && ]] is syntax error
[[ && ]]
echo status=$?
# stdout-json: ""
# status: 2
# OK mksh status: 1
## stdout-json: ""
## status: 2
## OK mksh status: 1
View
@@ -1,37 +1,37 @@
#!/usr/bin/env bash
### (( )) result
#### (( )) result
(( 1 )) && echo True
(( 0 )) || echo False
# stdout-json: "True\nFalse\n"
## stdout-json: "True\nFalse\n"
### negative number is true
#### negative number is true
(( -1 )) && echo True
# stdout: True
## stdout: True
### (( )) in if statement
#### (( )) in if statement
if (( 3 > 2)); then
echo True
fi
# stdout: True
## stdout: True
### (( ))
#### (( ))
# What is the difference with this and let? One difference: spaces are allowed.
(( x = 1 ))
(( y = x + 2 ))
echo $x $y
# stdout: 1 3
## stdout: 1 3
### (( )) with arrays
#### (( )) with arrays
a=(4 5 6)
(( sum = a[0] + a[1] + a[2] ))
echo $sum
# stdout: 15
# OK zsh stdout: 9
## stdout: 15
## OK zsh stdout: 9
### (( )) with error
#### (( )) with error
(( a = 0 )) || echo false
(( b = 1 )) && echo true
(( c = -1 )) && echo true
echo $((a + b + c))
# stdout-json: "false\ntrue\ntrue\n0\n"
## stdout-json: "false\ntrue\ntrue\n0\n"
View
@@ -1,25 +1,25 @@
#!/usr/bin/env bash
### Empty do/done
#### Empty do/done
while false; do
done
echo empty
# stdout: empty
# OK dash/bash stdout-json: ""
# OK dash/bash status: 2
## stdout: empty
## OK dash/bash stdout-json: ""
## OK dash/bash status: 2
### Empty case/esac
#### Empty case/esac
case foo in
esac
echo empty
# stdout: empty
## stdout: empty
### Empty then/fi
#### Empty then/fi
if foo; then
fi
echo empty
# stdout: empty
# OK dash/bash stdout-json: ""
# OK dash/bash status: 2
# OK mksh stdout-json: ""
# OK mksh status: 1
## stdout: empty
## OK dash/bash stdout-json: ""
## OK dash/bash status: 2
## OK mksh stdout-json: ""
## OK mksh status: 1
View
@@ -9,7 +9,7 @@
# different.
# - ash has copied bash behavior!
### command sub: errexit ignored
#### command sub: errexit ignored
# This is the bash-specific bug here:
# https://blogs.janestreet.com/when-bash-scripts-bite/
# In bash 4.4, inherit_errexit should fix this.
@@ -26,7 +26,7 @@ one
status=0
## END
### command sub: errexit not ignored with strict-errexit
#### command sub: errexit not ignored with strict-errexit
set -o errexit
set -o strict-errexit || true
echo zero
@@ -46,7 +46,7 @@ one two
status=0
## END
### command sub: last command fails but keeps going and exit code is 0
#### command sub: last command fails but keeps going and exit code is 0
set -o errexit
echo $(echo one; false) # we lost the exit code
echo status=$?
@@ -55,7 +55,7 @@ one
status=0
## END
### global assignment with command sub: middle command fails
#### global assignment with command sub: middle command fails
set -o errexit
s=$(echo one; false; echo two;)
echo "$s"
@@ -68,14 +68,14 @@ two
## OK dash/mksh stdout-json: ""
## OK dash/mksh status: 1
### global assignment with command sub: last command fails and it aborts
#### global assignment with command sub: last command fails and it aborts
set -o errexit
s=$(echo one; false)
echo status=$?
## stdout-json: ""
## status: 1
### local: middle command fails and keeps going
#### local: middle command fails and keeps going
set -o errexit
f() {
echo good
@@ -96,7 +96,7 @@ status=0
one
## END
### local: last command fails and also keeps going
#### local: last command fails and also keeps going
set -o errexit
f() {
echo good
@@ -111,7 +111,7 @@ status=0
one
## END
### local and strict-errexit
#### local and strict-errexit
# I've run into this problem a lot.
set -o errexit
set -o strict-errexit || true # ignore error
@@ -122,7 +122,7 @@ f() {
echo $x
}
f
# status: 1
## status: 1
## STDOUT:
good
## END
@@ -137,7 +137,7 @@ one two
## N-I mksh status: 1
## N-I mksh stdout-json: ""
### global assignment when last status is failure
#### global assignment when last status is failure
# this is a bug I introduced
set -o errexit
[ -n "${BUILDDIR+x}" ] && _BUILDDIR=$BUILDDIR
@@ -147,7 +147,7 @@ echo status=$?
status=0
## END
### global assignment when last status is failure
#### global assignment when last status is failure
# this is a bug I introduced
set -o errexit
x=$(false) || true # from abuild
View
@@ -3,35 +3,35 @@
# Usage:
# ./errexit.test.sh <function name>
### errexit aborts early
#### errexit aborts early
set -o errexit
false
echo done
# stdout-json: ""
# status: 1
## stdout-json: ""
## status: 1
### errexit for nonexistent command
#### errexit for nonexistent command
set -o errexit
nonexistent__ZZ
echo done
# stdout-json: ""
# status: 127
## stdout-json: ""
## status: 127
### errexit aborts early on pipeline
#### errexit aborts early on pipeline
set -o errexit
echo hi | grep nonexistent
echo two
# stdout-json: ""
# status: 1
## stdout-json: ""
## status: 1
### errexit with { }
#### errexit with { }
# This aborts because it's not part of an if statement.
set -o errexit
{ echo one; false; echo two; }
# stdout: one
# status: 1
## stdout: one
## status: 1
### errexit with if and { }
#### errexit with if and { }
set -o errexit
if { echo one; false; echo two; }; then
echo three
@@ -45,52 +45,52 @@ three
four
## END
### errexit with ||
#### errexit with ||
set -o errexit
echo hi | grep nonexistent || echo ok
# stdout: ok
# status: 0
## stdout: ok
## status: 0
### errexit with &&
#### errexit with &&
set -o errexit
echo ok && echo hi | grep nonexistent
# stdout: ok
# status: 1
## stdout: ok
## status: 1
### errexit test && -- from gen-module-init
#### errexit test && -- from gen-module-init
set -o errexit
test "$mod" = readline && echo "#endif"
echo status=$?
# stdout: status=1
## stdout: status=1
### errexit test && and fail
#### errexit test && and fail
set -o errexit
test -n X && false
echo status=$?
# stdout-json: ""
# status: 1
## stdout-json: ""
## status: 1
### errexit and loop
#### errexit and loop
set -o errexit
for x in 1 2 3; do
test $x = 2 && echo "hi $x"
done
# stdout: hi 2
# status: 1
## stdout: hi 2
## status: 1
### errexit and brace group { }
#### errexit and brace group { }
set -o errexit
{ test no = yes && echo hi; }
echo status=$?
# stdout: status=1
## stdout: status=1
### errexit and time { }
#### errexit and time { }
set -o errexit
time false
echo status=$?
# status: 1
## status: 1
### errexit with !
#### errexit with !
set -o errexit
echo one
! true
@@ -103,7 +103,7 @@ two
three
## END
### errexit with ! and ;
#### errexit with ! and ;
# AST has extra Sentence nodes; there was a REGRESSION here.
set -o errexit; echo one; ! true; echo two; ! false; echo three
## STDOUT:
@@ -112,7 +112,7 @@ two
three
## END
### errexit with while/until
#### errexit with while/until
set -o errexit
while false; do
echo ok
@@ -121,25 +121,25 @@ until false; do
echo ok # do this once then exit loop
break
done
# stdout: ok
# status: 0
## stdout: ok
## status: 0
### errexit with (( ))
#### errexit with (( ))
# from http://mywiki.wooledge.org/BashFAQ/105, this changed between verisons.
# ash says that 'i++' is not found, but it doesn't exit. I guess this is the
# subshell problem?
set -o errexit
i=0
(( i++ ))
echo done
# stdout-json: ""
# status: 1
# N-I dash status: 127
# N-I dash stdout-json: ""
# BUG ash status: 0
# BUG ash stdout: done
## stdout-json: ""
## status: 1
## N-I dash status: 127
## N-I dash stdout-json: ""
## BUG ash status: 0
## BUG ash stdout: done
### errexit with subshell
#### errexit with subshell
set -o errexit
( echo one; false; echo two; )
echo three
@@ -152,7 +152,7 @@ one
three
## END
### setting errexit while it's being ignored
#### setting errexit while it's being ignored
# ignored and then set again
set -o errexit
# osh aborts early here
@@ -176,7 +176,7 @@ echo 7
6
## END
### setting errexit in a subshell works but doesn't affect parent shell
#### setting errexit in a subshell works but doesn't affect parent shell
( echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4; )
echo 5
false
@@ -189,7 +189,7 @@ echo 6
6
## END
### setting errexit while it's being ignored in a subshell
#### setting errexit while it's being ignored in a subshell
set -o errexit
if ( echo 1; false; echo 2; set -o errexit; echo 3; false; echo 4 ); then
echo 5;
@@ -211,7 +211,7 @@ echo 7
6
## END
### errexit double guard
#### errexit double guard
# OSH bug fix. ErrExit needs a counter, not a boolean.
set -o errexit
if { ! false; false; true; } then
@@ -224,7 +224,7 @@ echo done
true
## END
### background processes respect errexit
#### background processes respect errexit
set -o errexit
{ echo one; false; echo two; exit 42; } &
wait $!
@@ -233,7 +233,7 @@ wait $!
one
## END
### pipeline process respects errexit
#### pipeline process respects errexit
set -o errexit
# It is respected here.
{ echo one; false; echo two; } | cat
@@ -244,7 +244,7 @@ set -o errexit
false
done
echo four
# status: 1
## status: 1
## STDOUT:
one
[three]
View
@@ -2,12 +2,12 @@
#
# Tests that explore parsing corner cases.
### Length of length of ARGS!
#### Length of length of ARGS!
func() { echo ${##}; }
func 0 1 2 3 4 5 6 7 8
# stdout: 1
## stdout: 1
### Length of length of ARGS! 2 digit
#### Length of length of ARGS! 2 digit
func() { echo ${##}; }
func 0 1 2 3 4 5 6 7 8 9
# stdout: 2
## stdout: 2
View
@@ -26,123 +26,123 @@
# @(pattern-list): Matches exactly one of the patterns
# !(pattern-list): Matches anything EXCEPT any of the patterns
### @() extended glob
#### @() extended glob
shopt -s extglob
touch _tmp/{foo,bar}.cc _tmp/{foo,bar,baz}.h
echo _tmp/@(*.cc|*.h)
# stdout: _tmp/bar.cc _tmp/bar.h _tmp/baz.h _tmp/foo.cc _tmp/foo.h
## stdout: _tmp/bar.cc _tmp/bar.h _tmp/baz.h _tmp/foo.cc _tmp/foo.h
### ?() extended glob
#### ?() extended glob
# matches the empty string too
shopt -s extglob
touch _tmp/{foo,bar}.cc _tmp/{foo,bar,baz}.h
echo _tmp/?(*.cc|*.h)
# stdout: _tmp/bar.cc _tmp/bar.h _tmp/baz.h _tmp/foo.cc _tmp/foo.h
## stdout: _tmp/bar.cc _tmp/bar.h _tmp/baz.h _tmp/foo.cc _tmp/foo.h
### *() matches multiple copies
#### *() matches multiple copies
shopt -s extglob
mkdir -p _tmp/eg1
touch _tmp/eg1/One _tmp/eg1/OneOne _tmp/eg1/TwoTwo _tmp/eg1/OneTwo
echo _tmp/eg1/*(One|Two)
# stdout: _tmp/eg1/One _tmp/eg1/OneOne _tmp/eg1/OneTwo _tmp/eg1/TwoTwo
## stdout: _tmp/eg1/One _tmp/eg1/OneOne _tmp/eg1/OneTwo _tmp/eg1/TwoTwo
### !(*.h) to match everything except headers
#### !(*.h) to match everything except headers
shopt -s extglob
mkdir -p _tmp/extglob2
touch _tmp/extglob2/{foo,bar}.cc _tmp/extglob2/{foo,bar,baz}.h
echo _tmp/extglob2/!(*.h)
# stdout: _tmp/extglob2/bar.cc _tmp/extglob2/foo.cc
## stdout: _tmp/extglob2/bar.cc _tmp/extglob2/foo.cc
### glob spaces
#### glob spaces
shopt -s extglob
mkdir -p _tmp/eg4
touch _tmp/eg4/a '_tmp/eg4/a b' _tmp/eg4/foo
argv.py _tmp/eg4/@(a b|foo)
# stdout: ['_tmp/eg4/a b', '_tmp/eg4/foo']
## stdout: ['_tmp/eg4/a b', '_tmp/eg4/foo']
### glob other punctuation chars (lexer mode)
#### glob other punctuation chars (lexer mode)
# mksh sorts them differently
shopt -s extglob
mkdir -p _tmp/eg5
cd _tmp/eg5
touch __{'<>','{}','|','#','&&'}
argv.py @('__<>'|__{}|__\||__#|__&&)
# stdout: ['__<>', '__|', '__{}', '__&&', '__#']
# OK mksh stdout: ['__#', '__&&', '__<>', '__{}', '__|']
## stdout: ['__<>', '__|', '__{}', '__&&', '__#']
## OK mksh stdout: ['__#', '__&&', '__<>', '__{}', '__|']
### @ matches exactly one
#### @ matches exactly one
[[ --verbose == --@(help|verbose) ]] && echo TRUE
[[ --oops == --@(help|verbose) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
## stdout-json: "TRUE\nFALSE\n"
### ? matches 0 or 1
#### ? matches 0 or 1
[[ -- == --?(help|verbose) ]] && echo TRUE
[[ --oops == --?(help|verbose) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
## stdout-json: "TRUE\nFALSE\n"
### + matches 1 or more
#### + matches 1 or more
[[ --helphelp == --+(help|verbose) ]] && echo TRUE
[[ -- == --+(help|verbose) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
## stdout-json: "TRUE\nFALSE\n"
### * matches 0 or more
#### * matches 0 or more
[[ -- == --*(help|verbose) ]] && echo TRUE
[[ --oops == --*(help|verbose) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
## stdout-json: "TRUE\nFALSE\n"
### simple repetition with *(foo) and +(Foo)
#### simple repetition with *(foo) and +(Foo)
[[ foofoo == *(foo) ]] && echo TRUE
[[ foofoo == +(foo) ]] && echo TRUE
# stdout-json: "TRUE\nTRUE\n"
## stdout-json: "TRUE\nTRUE\n"
### ! matches none
#### ! matches none
[[ --oops == --!(help|verbose) ]] && echo TRUE
[[ --help == --!(help|verbose) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
## stdout-json: "TRUE\nFALSE\n"
### @() with variable arms
#### @() with variable arms
choice1='help'
choice2='verbose'
[[ --verbose == --@($choice1|$choice2) ]] && echo TRUE
[[ --oops == --@($choice1|$choice2) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
## stdout-json: "TRUE\nFALSE\n"
### match is anchored
#### match is anchored
[[ foo_ == @(foo) ]] || echo FALSE
[[ _foo == @(foo) ]] || echo FALSE
[[ foo == @(foo) ]] && echo TRUE
# stdout-json: "FALSE\nFALSE\nTRUE\n"
## stdout-json: "FALSE\nFALSE\nTRUE\n"
### repeated match is anchored
#### repeated match is anchored
[[ foofoo_ == +(foo) ]] || echo FALSE
[[ _foofoo == +(foo) ]] || echo FALSE
[[ foofoo == +(foo) ]] && echo TRUE
# stdout-json: "FALSE\nFALSE\nTRUE\n"
## stdout-json: "FALSE\nFALSE\nTRUE\n"
### repetition with glob
#### repetition with glob
# NOTE that * means two different things here
[[ foofoo_foo__foo___ == *(foo*) ]] && echo TRUE
[[ Xoofoo_foo__foo___ == *(foo*) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
## stdout-json: "TRUE\nFALSE\n"
### No brace expansion in ==
#### No brace expansion in ==
[[ --X{a,b}X == --@(help|X{a,b}X) ]] && echo TRUE
[[ --oops == --@(help|X{a,b}X) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
## stdout-json: "TRUE\nFALSE\n"
### adjacent extglob
#### adjacent extglob
[[ --help == @(--|++)@(help|verbose) ]] && echo TRUE
[[ ++verbose == @(--|++)@(help|verbose) ]] && echo TRUE
# stdout-json: "TRUE\nTRUE\n"
## stdout-json: "TRUE\nTRUE\n"
### nested extglob
#### nested extglob
[[ --help == --@(help|verbose=@(1|2)) ]] && echo TRUE
[[ --verbose=1 == --@(help|verbose=@(1|2)) ]] && echo TRUE
[[ --verbose=2 == --@(help|verbose=@(1|2)) ]] && echo TRUE
[[ --verbose == --@(help|verbose=@(1|2)) ]] || echo FALSE
# stdout-json: "TRUE\nTRUE\nTRUE\nFALSE\n"
## stdout-json: "TRUE\nTRUE\nTRUE\nFALSE\n"
### extglob in variable
#### extglob in variable
shopt -s extglob
g=--@(help|verbose)
quoted='--@(help|verbose)'
@@ -151,34 +151,34 @@ quoted='--@(help|verbose)'
[[ -- == $g ]] || echo FALSE
[[ --help == $q ]] || echo FALSE
[[ -- == $q ]] || echo FALSE
# stdout-json: "TRUE\nTRUE\nFALSE\nFALSE\nFALSE\n"
# N-I mksh stdout-json: "FALSE\nFALSE\nFALSE\n"
## stdout-json: "TRUE\nTRUE\nFALSE\nFALSE\nFALSE\n"
## N-I mksh stdout-json: "FALSE\nFALSE\nFALSE\n"
### extglob empty string
#### extglob empty string
shopt -s extglob
[[ '' == @(foo|bar) ]] || echo FALSE
[[ '' == @(foo||bar) ]] && echo TRUE
# stdout-json: "FALSE\nTRUE\n"
## stdout-json: "FALSE\nTRUE\n"
### extglob empty pattern
#### extglob empty pattern
shopt -s extglob
[[ '' == @() ]] && echo TRUE
[[ '' == @(||) ]] && echo TRUE
[[ X == @() ]] || echo FALSE
[[ '|' == @(||) ]] || echo FALSE
# stdout-json: "TRUE\nTRUE\nFALSE\nFALSE\n"
## stdout-json: "TRUE\nTRUE\nFALSE\nFALSE\n"
### printing extglob in variable
#### printing extglob in variable
# mksh does static parsing so it doesn't like this?
shopt -s extglob
mkdir -p _tmp/eg3
touch _tmp/eg3/{foo,bar}
g=_tmp/eg3/@(foo|bar)
echo $g "$g" # quoting inhibits globbing
# stdout: _tmp/eg3/bar _tmp/eg3/foo _tmp/eg3/@(foo|bar)
# N-I mksh stdout: _tmp/eg3/@(foo|bar) _tmp/eg3/@(foo|bar)
## stdout: _tmp/eg3/bar _tmp/eg3/foo _tmp/eg3/@(foo|bar)
## N-I mksh stdout: _tmp/eg3/@(foo|bar) _tmp/eg3/@(foo|bar)
### case with extglob
#### case with extglob
shopt -s extglob
for word in --help --verbose --unmatched -- -zxzx -; do
case $word in
@@ -204,31 +204,31 @@ for word in --help --verbose --unmatched -- -zxzx -; do
;;
esac
done
# stdout-json: "A\nA\nU\nB\nC\nD\n"
## stdout-json: "A\nA\nU\nB\nC\nD\n"
### Without shopt -s extglob
#### Without shopt -s extglob
empty=''
str='x'
[[ $empty == !($str) ]] && echo TRUE # test glob match
[[ $str == !($str) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
## stdout-json: "TRUE\nFALSE\n"
### Turning extglob on changes the meaning of [[ !(str) ]] in bash
#### Turning extglob on changes the meaning of [[ !(str) ]] in bash
empty=''
str='x'
[[ !($empty) ]] && echo TRUE # test if $empty is empty
[[ !($str) ]] || echo FALSE # test if $str is empty
shopt -s extglob # mksh doesn't have this
[[ !($empty) ]] && echo TRUE # negated glob
[[ !($str) ]] && echo TRUE # negated glob
# stdout-json: "TRUE\nFALSE\nTRUE\nTRUE\n"
# OK mksh stdout-json: "TRUE\nTRUE\nTRUE\n"
## stdout-json: "TRUE\nFALSE\nTRUE\nTRUE\n"
## OK mksh stdout-json: "TRUE\nTRUE\nTRUE\n"
### With extglob on, !($str) on the left or right of == has different meanings
#### With extglob on, !($str) on the left or right of == has different meanings
shopt -s extglob
empty=''
str='x'
[[ 1 == !($str) ]] && echo TRUE # glob match
[[ !($str) == 1 ]] || echo FALSE # test if empty
# NOTE: There cannot be a space between ! and (?
# stdout-json: "TRUE\nFALSE\n"
## stdout-json: "TRUE\nFALSE\n"
View
@@ -3,7 +3,7 @@
# Constructs borrowed from ksh. Hm I didn't realize zsh also implements these!
# mksh implements most too.
### C-style for loop
#### C-style for loop
n=10
for ((a=1; a <= n ; a++)) # Double parentheses, and naked 'n'
do
@@ -24,7 +24,7 @@ done # A construct borrowed from ksh93.
## N-I mksh status: 1
## N-I mksh stdout-json: ""
### For loop with and without semicolon
#### For loop with and without semicolon
for ((a=1; a <= 3; a++)); do
echo $a
done # A construct borrowed from ksh93.
@@ -42,7 +42,7 @@ done # A construct borrowed from ksh93.
## N-I mksh status: 1
## N-I mksh stdout-json: ""
### For loop with empty head
#### For loop with empty head
a=1
for ((;;)); do
if test $a = 4; then
View
@@ -1,35 +1,35 @@
#!/usr/bin/env bash
### Incomplete Function
# code: foo()
# status: 2
# BUG mksh status: 0
#### Incomplete Function
## code: foo()
## status: 2
## BUG mksh status: 0
### Incomplete Function 2
# code: foo() {
# status: 2
# OK mksh status: 1
#### Incomplete Function 2
## code: foo() {
## status: 2
## OK mksh status: 1
### Bad function
# code: foo(ls)
# status: 2
# OK mksh status: 1
#### Bad function
## code: foo(ls)
## status: 2
## OK mksh status: 1
### Unbraced function body.
#### Unbraced function body.
# dash allows this, but bash does not. The POSIX grammar might not allow
# this? Because a function body needs a compound command.
# function_body : compound_command
# | compound_command redirect_list /* Apply rule 9 */
# code: one_line() ls; one_line;
# status: 0
# OK bash/osh status: 2
## code: one_line() ls; one_line;
## status: 0
## OK bash/osh status: 2
### Function with spaces, to see if ( and ) are separate tokens.
#### Function with spaces, to see if ( and ) are separate tokens.
# NOTE: Newline after ( is not OK.
func ( ) { echo in-func; }; func
# stdout: in-func
## stdout: in-func
### subshell function
#### subshell function
# bash allows this.
i=0
j=0
@@ -38,62 +38,62 @@ inc_subshell() ( j=$((j+5)); )
inc
inc_subshell
echo $i $j
# stdout: 5 0
## stdout: 5 0
### Hard case, function with } token in it
#### Hard case, function with } token in it
rbrace() { echo }; }; rbrace
# stdout: }
## stdout: }
### . in function name
#### . in function name
# bash accepts; dash doesn't
func-name.ext ( ) { echo func-name.ext; }
func-name.ext
# stdout: func-name.ext
# OK dash status: 2
# OK dash stdout-json: ""
## stdout: func-name.ext
## OK dash status: 2
## OK dash stdout-json: ""
### = in function name
#### = in function name
# WOW, bash is so lenient. foo=bar is a command, I suppose. I think I'm doing
# to disallow this one.
func-name=ext ( ) { echo func-name=ext; }
func-name=ext
# stdout: func-name=ext
# OK dash status: 2
# OK dash stdout-json: ""
# OK mksh status: 1
# OK mksh stdout-json: ""
## stdout: func-name=ext
## OK dash status: 2
## OK dash stdout-json: ""
## OK mksh status: 1
## OK mksh stdout-json: ""
### Function name with $
#### Function name with $
# bash allows this; dash doesn't.
foo$bar() { ls ; }
# status: 2
# OK bash/mksh status: 1
## status: 2
## OK bash/mksh status: 1
### Function name with !
#### Function name with !
# bash allows this; dash doesn't.
foo!bar() { ls ; }
# status: 0
# OK dash status: 2
## status: 0
## OK dash status: 2
### Function name with -
#### Function name with -
# bash allows this; dash doesn't.
foo-bar() { ls ; }
# status: 0
# OK dash status: 2
## status: 0
## OK dash status: 2
### Break after ) is OK.
#### Break after ) is OK.
# newline is always a token in "normal" state.
echo hi; func ( )
{ echo in-func; }
func
# stdout-json: "hi\nin-func\n"
## stdout-json: "hi\nin-func\n"
### Nested definition
#### Nested definition
# A function definition is a command, so it can be nested
func() {
nested_func() { echo nested; }
nested_func
}
func
# stdout: nested
## stdout: nested
View
@@ -1,32 +1,32 @@
#!/usr/bin/env bash
### Locals don't leak
#### Locals don't leak
f() {
local f_var=f_var
}
f
echo $f_var
# stdout:
## stdout:
### Globals leak
#### Globals leak
f() {
f_var=f_var
}
f
echo $f_var
# stdout: f_var
## stdout: f_var
### Return statement
#### Return statement
f() {
echo one
return 42
echo two
}
f
# stdout: one
# status: 42
## stdout: one
## status: 42
### Dynamic Scope
#### Dynamic Scope
f() {
echo $g_var
}
@@ -35,9 +35,9 @@ g() {
f
}
g
# stdout: g_var
## stdout: g_var
### Dynamic Scope Mutation (wow this is bad)
#### Dynamic Scope Mutation (wow this is bad)
f() {
g_var=f_mutation
}
@@ -47,19 +47,19 @@ g() {
echo "g: $g_var"
}
g
# stdout: g: f_mutation
## stdout: g: f_mutation
### Assign local separately
#### Assign local separately
f() {
local f
f='new-value'
echo "[$f]"
}
f
# stdout: [new-value]
# status: 0
## stdout: [new-value]
## status: 0
### Assign a local and global on same line
#### Assign a local and global on same line
myglobal=
f() {
local mylocal
@@ -68,5 +68,5 @@ f() {
}
f
echo "[$mylocal $myglobal]"
# stdout-json: "[L G]\n[ G]\n"
# status: 0
## stdout-json: "[L G]\n[ G]\n"
## status: 0
View
@@ -2,193 +2,193 @@
#
# NOTE: Could move spec/03-glob.sh here.
### glob double quote escape
#### glob double quote escape
echo "*.sh"
# stdout: *.sh
## stdout: *.sh
### glob single quote escape
#### glob single quote escape
echo "*.sh"
# stdout: *.sh
## stdout: *.sh
### glob backslash escape
#### glob backslash escape
echo \*.sh
# stdout: *.sh
## stdout: *.sh
### 1 char glob
#### 1 char glob
echo [b]in
# stdout: bin
## stdout: bin
### 0 char glob -- does NOT work
#### 0 char glob -- does NOT work
echo []bin
# stdout: []bin
## stdout: []bin
### looks like glob at the start, but isn't
#### looks like glob at the start, but isn't
echo [bin
# stdout: [bin
## stdout: [bin
### looks like glob plus negation at the start, but isn't
#### looks like glob plus negation at the start, but isn't
echo [!bin
# stdout: [!bin
## stdout: [!bin
### glob can expand to command and arg
#### glob can expand to command and arg
spec/testdata/echo.s[hz]
# stdout: spec/testdata/echo.sz
## stdout: spec/testdata/echo.sz
### glob after var expansion
#### glob after var expansion
touch _tmp/a.A _tmp/aa.A _tmp/b.B
f="_tmp/*.A"
g="$f _tmp/*.B"
echo $g
# stdout: _tmp/a.A _tmp/aa.A _tmp/b.B
## stdout: _tmp/a.A _tmp/aa.A _tmp/b.B
### quoted var expansion with glob meta characters
#### quoted var expansion with glob meta characters
touch _tmp/a.A _tmp/aa.A _tmp/b.B
f="_tmp/*.A"
echo "[ $f ]"
# stdout: [ _tmp/*.A ]
## stdout: [ _tmp/*.A ]
### glob after "$@" expansion
#### glob after "$@" expansion
func() {
echo "$@"
}
func '_tmp/*.B'
# stdout: _tmp/*.B
## stdout: _tmp/*.B
### glob after $@ expansion
#### glob after $@ expansion
func() {
echo $@
}
func '_tmp/*.B'
# stdout: _tmp/b.B
## stdout: _tmp/b.B
### no glob after ~ expansion
#### no glob after ~ expansion
HOME=*
echo ~/*.py
# stdout: */*.py
## stdout: */*.py
### store literal globs in array then expand
#### store literal globs in array then expand
touch _tmp/a.A _tmp/aa.A _tmp/b.B
g=("_tmp/*.A" "_tmp/*.B")
echo ${g[@]}
# stdout: _tmp/a.A _tmp/aa.A _tmp/b.B
# N-I dash/ash stdout-json: ""
# N-I dash/ash status: 2
## stdout: _tmp/a.A _tmp/aa.A _tmp/b.B
## N-I dash/ash stdout-json: ""
## N-I dash/ash status: 2
### glob inside array
#### glob inside array
touch _tmp/a.A _tmp/aa.A _tmp/b.B
g=(_tmp/*.A _tmp/*.B)
echo "${g[@]}"
# stdout: _tmp/a.A _tmp/aa.A _tmp/b.B
# N-I dash/ash stdout-json: ""
# N-I dash/ash status: 2
## stdout: _tmp/a.A _tmp/aa.A _tmp/b.B
## N-I dash/ash stdout-json: ""
## N-I dash/ash status: 2
### glob with escaped - in char class
#### glob with escaped - in char class
touch _tmp/foo.-
touch _tmp/c.C
echo _tmp/*.[C-D] _tmp/*.[C\-D]
# stdout: _tmp/c.C _tmp/c.C _tmp/foo.-
## stdout: _tmp/c.C _tmp/c.C _tmp/foo.-
### glob with char class expression
#### glob with char class expression
# note: mksh doesn't support [[:punct:]] ?
touch _tmp/e.E _tmp/foo.-
echo _tmp/*.[[:punct:]E]
# stdout: _tmp/e.E _tmp/foo.-
# BUG mksh stdout: _tmp/*.[[:punct:]E]
## stdout: _tmp/e.E _tmp/foo.-
## BUG mksh stdout: _tmp/*.[[:punct:]E]
### glob double quotes
#### glob double quotes
# note: mksh doesn't support [[:punct:]] ?
touch _tmp/\"quoted.py\"
echo _tmp/\"*.py\"
# stdout: _tmp/"quoted.py"
## stdout: _tmp/"quoted.py"
### glob escaped
#### glob escaped
# - mksh doesn't support [[:punct:]] ?
# - python shell fails because \[ not supported!
touch _tmp/\[abc\] _tmp/\?
echo _tmp/\[???\] _tmp/\?
# stdout: _tmp/[abc] _tmp/?
## stdout: _tmp/[abc] _tmp/?
### : escaped
#### : escaped
touch _tmp/foo.-
echo _tmp/*.[[:punct:]] _tmp/*.[[:punct\:]]
# stdout: _tmp/foo.- _tmp/*.[[:punct:]]
# BUG mksh stdout: _tmp/*.[[:punct:]] _tmp/*.[[:punct:]]
## stdout: _tmp/foo.- _tmp/*.[[:punct:]]
## BUG mksh stdout: _tmp/*.[[:punct:]] _tmp/*.[[:punct:]]
### Redirect to glob, not evaluated
#### Redirect to glob, not evaluated
# This writes to *.F, not foo.F
rm _tmp/*.F
touch _tmp/f.F
echo foo > _tmp/*.F
cat '_tmp/*.F'
# status: 0
# stdout: foo
# BUG bash status: 1
# BUG bash stdout-json: ""
## status: 0
## stdout: foo
## BUG bash status: 1
## BUG bash stdout-json: ""
### Glob after var manipulation
#### Glob after var manipulation
touch _tmp/foo.zzz _tmp/bar.zzz
g='_tmp/*.zzzZ'
echo $g ${g%Z}
# stdout: _tmp/*.zzzZ _tmp/bar.zzz _tmp/foo.zzz
## stdout: _tmp/*.zzzZ _tmp/bar.zzz _tmp/foo.zzz
### Glob after part joining
#### Glob after part joining
touch _tmp/foo.yyy _tmp/bar.yyy
g='_tmp/*.yy'
echo $g ${g}y
# stdout: _tmp/*.yy _tmp/bar.yyy _tmp/foo.yyy
## stdout: _tmp/*.yy _tmp/bar.yyy _tmp/foo.yyy
### Glob flags on file system
#### Glob flags on file system
touch _tmp/-n _tmp/zzzzz
cd _tmp
echo -* hello zzzz?
# stdout-json: "hello zzzzz"
## stdout-json: "hello zzzzz"
### set -o noglob
#### set -o noglob
touch _tmp/spec-tmp/a.zz _tmp/spec-tmp/b.zz
echo _tmp/spec-tmp/*.zz
set -o noglob
echo _tmp/spec-tmp/*.zz
# stdout-json: "_tmp/spec-tmp/a.zz _tmp/spec-tmp/b.zz\n_tmp/spec-tmp/*.zz\n"
## stdout-json: "_tmp/spec-tmp/a.zz _tmp/spec-tmp/b.zz\n_tmp/spec-tmp/*.zz\n"
### shopt -s nullglob
#### shopt -s nullglob
argv.py _tmp/spec-tmp/*.nonexistent
shopt -s nullglob
argv.py _tmp/spec-tmp/*.nonexistent
# stdout-json: "['_tmp/spec-tmp/*.nonexistent']\n[]\n"
# N-I dash/mksh/ash stdout-json: "['_tmp/spec-tmp/*.nonexistent']\n['_tmp/spec-tmp/*.nonexistent']\n"
## stdout-json: "['_tmp/spec-tmp/*.nonexistent']\n[]\n"
## N-I dash/mksh/ash stdout-json: "['_tmp/spec-tmp/*.nonexistent']\n['_tmp/spec-tmp/*.nonexistent']\n"
### shopt -s failglob
#### shopt -s failglob
argv.py *.ZZ
shopt -s failglob
argv.py *.ZZ # nothing is printed, not []
echo status=$?
# stdout-json: "['*.ZZ']\nstatus=1\n"
# N-I dash/mksh/ash stdout-json: "['*.ZZ']\n['*.ZZ']\nstatus=0\n"
## stdout-json: "['*.ZZ']\nstatus=1\n"
## N-I dash/mksh/ash stdout-json: "['*.ZZ']\n['*.ZZ']\nstatus=0\n"
### Don't glob flags on file system with GLOBIGNORE
#### Don't glob flags on file system with GLOBIGNORE
# This is a bash-specific extension.
expr $0 : '.*/osh$' >/dev/null && exit 99 # disabled until cd implemented
touch _tmp/-n _tmp/zzzzz
cd _tmp # this fail in osh
GLOBIGNORE=-*:zzzzz # colon-separated pattern list
echo -* hello zzzz?
# stdout-json: "-* hello zzzz?\n"
# N-I dash/mksh/ash stdout-json: "hello zzzzz"
# status: 0
## stdout-json: "-* hello zzzz?\n"
## N-I dash/mksh/ash stdout-json: "hello zzzzz"
## status: 0
### Splitting/Globbing doesn't happen on local assignment
#### Splitting/Globbing doesn't happen on local assignment
f() {
# Dash splits words and globs before handing it to the 'local' builtin. But
# ash doesn't!
local foo=$1
echo "$foo"
}
f 'void *'
# stdout: void *
# BUG dash stdout-json: ""
# BUG dash status: 2
## stdout: void *
## BUG dash stdout-json: ""
## BUG dash status: 2
### Glob of unescaped [[] and []]
#### Glob of unescaped [[] and []]
touch $TMP/[ $TMP/]
cd $TMP
echo [\[z] [\]z] # the right way to do it
@@ -198,7 +198,7 @@ echo [[z] []z] # also accepted
[ ]
## END
### Glob of negated unescaped [[] and []]
#### Glob of negated unescaped [[] and []]
touch $TMP/_G
cd $TMP
echo _[^\[z] _[^\]z] # the right way to do it
@@ -212,7 +212,7 @@ _[^[z] _[^]z]
_[^[z] _[^]z]
## END
### PatSub of unescaped [[] and []]
#### PatSub of unescaped [[] and []]
x='[foo]'
echo ${x//[\[z]/<} # the right way to do it
echo ${x//[\]z]/>}
@@ -227,7 +227,7 @@ echo ${x//[]z]/>}
## N-I dash stdout-json: ""
## N-I dash status: 2
### PatSub of negated unescaped [[] and []]
#### PatSub of negated unescaped [[] and []]
x='[foo]'
echo ${x//[^\[z]/<} # the right way to do it
echo ${x//[^\]z]/>}
View
@@ -1,33 +1,33 @@
#!/usr/bin/env bash
### Here string
#### Here string
cat <<< 'hi'
# stdout-json: "hi\n"
# N-I dash stdout-json: ""
# N-I dash status: 2
## stdout-json: "hi\n"
## N-I dash stdout-json: ""
## N-I dash status: 2
### Here string with $
#### Here string with $
cat <<< $'one\ntwo\n'
# stdout-json: "one\ntwo\n\n"
# N-I dash stdout-json: ""
# N-I dash status: 2
## stdout-json: "one\ntwo\n\n"
## N-I dash stdout-json: ""
## N-I dash status: 2
### Here redirect with explicit descriptor
#### Here redirect with explicit descriptor
# A space betwen 0 and <<EOF causes it to pass '0' as an arg to cat.
cat 0<<EOF
one
EOF
# stdout: one
## stdout: one
### Here doc from another input file descriptor
#### Here doc from another input file descriptor
# NOTE: OSH fails on descriptor 9, but not descriptor 8? Is this because of
# the Python VM? How to inspect state?
read_from_fd.py 8 8<<EOF
here doc on descriptor
EOF
# stdout: 8: here doc on descriptor
## stdout: 8: here doc on descriptor
### Multiple here docs with different descriptors
#### Multiple here docs with different descriptors
read_from_fd.py 0 3 <<EOF 3<<EOF3
fd0
EOF
@@ -38,37 +38,37 @@ EOF3
3: fd3
## END
### Here doc with bad var delimiter
#### Here doc with bad var delimiter
cat <<${a}
here
${a}
# stdout: here
## stdout: here
### Here doc with bad comsub delimiter
#### Here doc with bad comsub delimiter
# bash is OK with this; dash isn't. Should be a parse error.
cat <<$(a)
here
$(a)
# stdout-json: ""
# status: 2
# BUG bash stdout: here
# BUG bash status: 0
# OK mksh status: 1
## stdout-json: ""
## status: 2
## BUG bash stdout: here
## BUG bash status: 0
## OK mksh status: 1
### Here doc and < redirect -- last one wins
#### Here doc and < redirect -- last one wins
echo hello >$TMP/hello.txt # temporary fix
cat <<EOF <$TMP/hello.txt
here
EOF
# stdout: hello
## stdout: hello
### < redirect and here doc -- last one wins
#### < redirect and here doc -- last one wins
cat <$TMP/hello.txt <<EOF
here
EOF
# stdout: here
## stdout: here
### Here doc with var sub, command sub, arith sub
#### Here doc with var sub, command sub, arith sub
var=v
cat <<EOF
var: ${var}
@@ -81,7 +81,7 @@ command: hi
arith: 3
## END
### Here doc in middle. And redirects in the middle.
#### Here doc in middle. And redirects in the middle.
# This isn't specified by the POSIX grammar, but it's accepted by both dash and
# bash!
echo foo > _tmp/foo.txt
@@ -95,7 +95,7 @@ here
bar
## END
### Here doc line continuation
#### Here doc line continuation
cat <<EOF \
; echo two
one
@@ -105,14 +105,14 @@ one
two
## END
### Here doc with quote expansion in terminator
#### Here doc with quote expansion in terminator
cat <<'EOF'"2"
one
two
EOF2
# stdout-json: "one\ntwo\n"
## stdout-json: "one\ntwo\n"
### Here doc with multiline double quoted string
#### Here doc with multiline double quoted string
cat <<EOF; echo "two
three"
one
@@ -123,25 +123,25 @@ two
three
## END
### Two here docs -- first is ignored; second ones wins!
#### Two here docs -- first is ignored; second ones wins!
<<EOF1 cat <<EOF2
hello
EOF1
there
EOF2
# stdout: there
## stdout: there
### Here doc with line continuation, then pipe. Syntax error.
#### Here doc with line continuation, then pipe. Syntax error.
cat <<EOF \
1
2
3
EOF
| tac
# status: 2
# OK mksh status: 1
## status: 2
## OK mksh status: 1
### Here doc with pipe on first line
#### Here doc with pipe on first line
cat <<EOF | tac
1
2
@@ -153,7 +153,7 @@ EOF
1
## END
### Here doc with pipe continued on last line
#### Here doc with pipe continued on last line
cat <<EOF |
1
2
@@ -166,15 +166,15 @@ tac
1
## END
### Here doc with builtin 'read'
#### Here doc with builtin 'read'
# read can't be run in a subshell.
read v1 v2 <<EOF
val1 val2
EOF
echo =$v1= =$v2=
# stdout: =val1= =val2=
## stdout: =val1= =val2=
### Compound command here doc
#### Compound command here doc
while read line; do
echo X $line
done <<EOF
@@ -189,7 +189,7 @@ X 3
## END
### Here doc in while condition and here doc in body
#### Here doc in while condition and here doc in body
while cat <<E1 && cat <<E2; do cat <<E3; break; done
1
E1
@@ -203,7 +203,7 @@ E3
3
## END
### Here doc in while condition and here doc in body on multiple lines
#### Here doc in while condition and here doc in body on multiple lines
while cat <<E1 && cat <<E2
1
E1
@@ -221,7 +221,7 @@ done
3
## END
### Here doc in while loop split up more
#### Here doc in while loop split up more
while cat <<E1
1
E1
@@ -242,53 +242,53 @@ done
3
## END
### Mixing << and <<-
#### Mixing << and <<-
cat <<-EOF; echo --; cat <<EOF2
one
EOF
two
EOF2
# stdout-json: "one\n--\ntwo\n"
## stdout-json: "one\n--\ntwo\n"
### Two compound commands with two here docs
#### Two compound commands with two here docs
while read line; do echo X $line; done <<EOF; echo ==; while read line; do echo Y $line; done <<EOF2
1
2
EOF
3
4
EOF2
# stdout-json: "X 1\nX 2\n==\nY 3\nY 4\n"
## stdout-json: "X 1\nX 2\n==\nY 3\nY 4\n"
### Function def and execution with here doc
#### Function def and execution with here doc
func() { cat; } <<EOF; echo before; func; echo after
1
2
EOF
# stdout-json: "before\n1\n2\nafter\n"
## stdout-json: "before\n1\n2\nafter\n"
### Here doc as command prefix
#### Here doc as command prefix
<<EOF tac
1
2
3
EOF
# stdout-json: "3\n2\n1\n"
## stdout-json: "3\n2\n1\n"
# NOTE that you can have redirection AFTER the here doc thing. And you don't
# need a space! Those are operators.
#
# POSIX doesn't seem to have this? They have io_file, which is for
# filenames, and io_here, which is here doc. But about 1>&2 syntax? Geez.
### Redirect after here doc
#### Redirect after here doc
cat <<EOF 1>&2
out
EOF
# stderr: out
## stderr: out
### here doc stripping tabs
#### here doc stripping tabs
cat <<-EOF
1
2
@@ -300,15 +300,15 @@ EOF
3
## END
### Here doc within subshell with boolean
#### Here doc within subshell with boolean
[[ $(cat <<EOF
foo
EOF
) == foo ]]; echo $?
# stdout: 0
# N-I dash stdout: 127
## stdout: 0
## N-I dash stdout: 127
### Here Doc in if condition
#### Here Doc in if condition
if cat <<EOF; then
here doc in IF CONDITION
EOF
@@ -319,7 +319,7 @@ here doc in IF CONDITION
THEN executed
## END
### Multiple here docs in pipeline
#### Multiple here docs in pipeline
# SKIPPED: hangs with osh on Debian
# The second instance reads its stdin from the pipe, and fd 5 from a here doc.
read_from_fd.py 3 3<<EOF3 | read_from_fd.py 0 5 5<<EOF5
@@ -332,7 +332,7 @@ EOF5
5: fd5
## END
### Multiple here docs in pipeline on multiple lines
#### Multiple here docs in pipeline on multiple lines
# SKIPPED: hangs with osh on Debian
# The second instance reads its stdin from the pipe, and fd 5 from a here doc.
read_from_fd.py 3 3<<EOF3 |
View
@@ -2,31 +2,31 @@
#
# Test the if statement
### If
#### If
if true; then
echo if
fi
# stdout: if
## stdout: if
### else
#### else
if false; then
echo if
else
echo else
fi
# stdout: else
## stdout: else
### elif
#### elif
if (( 0 )); then
echo if
elif true; then
echo elif
else
echo else
fi
# stdout: elif
## stdout: elif
### Long style
#### Long style
if [[ 0 -eq 1 ]]
then
echo if
@@ -38,34 +38,34 @@ else
echo else
echo else
fi
# stdout: elif
## stdout: elif
# Weird case from bash-help mailing list.
#
# "Evaluations of backticks in if statements". It doesn't relate to if
# statements but to $?, since && and || behave the same way.
### If empty command
#### If empty command
if ''; then echo TRUE; else echo FALSE; fi
# stdout: FALSE
# status: 0
## stdout: FALSE
## status: 0
### If subshell true
#### If subshell true
if `true`; then echo TRUE; else echo FALSE; fi
# stdout: TRUE
# status: 0
## stdout: TRUE
## status: 0
### If subshell true WITH OUTPUT is different
#### If subshell true WITH OUTPUT is different
if `sh -c 'echo X; true'`; then echo TRUE; else echo FALSE; fi
# stdout: FALSE
# status: 0
## stdout: FALSE
## status: 0
### If subshell true WITH ARGUMENT
#### If subshell true WITH ARGUMENT
if `true` X; then echo TRUE; else echo FALSE; fi
# stdout: FALSE
# status: 0
## stdout: FALSE
## status: 0
### If subshell false
#### If subshell false
if `false`; then echo TRUE; else echo FALSE; fi
# stdout: FALSE
# status: 0
## stdout: FALSE
## status: 0
View
@@ -22,7 +22,7 @@
# Geez.
### ${FUNCNAME[@]} array
#### ${FUNCNAME[@]} array
g() {
argv.py "${FUNCNAME[@]}"
}
@@ -38,7 +38,7 @@ f
['f']
## END
### FUNCNAME with source
#### FUNCNAME with source
f() {
. spec/testdata/echo-funcname.sh
}
@@ -54,7 +54,7 @@ argv.py "${FUNCNAME[@]}"
[]
## END
### ${BASH_SOURCE[@]} is a stack of source files for function calls
#### ${BASH_SOURCE[@]} is a stack of source files for function calls
$SH spec/testdata/bash-source.sh
## STDOUT:
['begin F funcs', 'f', 'main']
@@ -68,7 +68,7 @@ $SH spec/testdata/bash-source.sh
['end F lines', '21', '0']
## END
### ${BASH_LINENO[@]} is a stack of line numbers for function calls
#### ${BASH_LINENO[@]} is a stack of line numbers for function calls
# note: it's CALLS, not DEFINITIONS.
g() {
argv.py G "${BASH_LINENO[@]}"
@@ -85,7 +85,7 @@ f # line 9
['end F', '9']
## END
### $LINENO is the current line, not line of function call
#### $LINENO is the current line, not line of function call
g() {
argv.py $LINENO # line 2
}
View
@@ -2,23 +2,23 @@
#
# let arithmetic.
### let
#### let
# NOTE: no spaces are allowed. How is this tokenized?
let x=1
let y=x+2
let z=y*3 # zsh treats this as a glob; bash doesn't
let z2='y*3' # both are OK with this
echo $x $y $z $z2
# stdout: 1 3 9 9
# OK zsh stdout-json: ""
# OK zsh status: 1
## stdout: 1 3 9 9
## OK zsh stdout-json: ""
## OK zsh status: 1
### let with ()
#### let with ()
let x=( 1 )
let y=( x + 2 )
let z=( y * 3 )
echo $x $y $z
# stdout: 1 3 9
# status: 0
# N-I mksh/zsh stdout-json: ""
# N-I mksh/zsh status: 1
## stdout: 1 3 9
## status: 0
## N-I mksh/zsh stdout-json: ""
## N-I mksh/zsh status: 1
View
@@ -1,109 +1,109 @@
#!/usr/bin/env bash
### implicit for loop
#### implicit for loop
# This is like "for i in $@".
func() {
for i; do
echo $i
done
}
func 1 2 3
# stdout-json: "1\n2\n3\n"
## stdout-json: "1\n2\n3\n"
### empty for loop (has "in")
#### empty for loop (has "in")
set -- 1 2 3
for i in ; do
echo $i
done
# stdout-json: ""
## stdout-json: ""
### for loop with invalid identifier
#### for loop with invalid identifier
# should be compile time error, but runtime error is OK too
for - in a b c; do
echo hi
done
# stdout-json: ""
# status: 2
# OK bash/mksh status: 1
## stdout-json: ""
## status: 2
## OK bash/mksh status: 1
### Tilde expansion within for loop
#### Tilde expansion within for loop
HOME=/home/bob
for name in ~/src ~/git; do
echo $name
done
# stdout-json: "/home/bob/src\n/home/bob/git\n"
## stdout-json: "/home/bob/src\n/home/bob/git\n"
### Brace Expansion within Array
#### Brace Expansion within Array
for i in -{a,b} {c,d}-; do
echo $i
done
# stdout-json: "-a\n-b\nc-\nd-\n"
# N-I dash stdout-json: "-{a,b}\n{c,d}-\n"
## stdout-json: "-a\n-b\nc-\nd-\n"
## N-I dash stdout-json: "-{a,b}\n{c,d}-\n"
### using loop var outside loop
#### using loop var outside loop
func() {
for i in a b c; do
echo $i
done
echo $i
}
func
# status: 0
# stdout-json: "a\nb\nc\nc\n"
## status: 0
## stdout-json: "a\nb\nc\nc\n"
### continue
#### continue
for i in a b c; do
echo $i
if test $i = b; then
continue
fi
echo $i
done
# status: 0
# stdout-json: "a\na\nb\nc\nc\n"
## status: 0
## stdout-json: "a\na\nb\nc\nc\n"
### break
#### break
for i in a b c; do
echo $i
if test $i = b; then
break
fi
done
# status: 0
# stdout-json: "a\nb\n"
## status: 0
## stdout-json: "a\nb\n"
### while in while condition
#### while in while condition
# This is a consequence of the grammar
while while true; do echo cond; break; done
do
echo body
break
done
# stdout-json: "cond\nbody\n"
## stdout-json: "cond\nbody\n"
### while in pipe
#### while in pipe
i=0
find tests/ | while read $path; do
i=$((i+1))
#echo $i
done
# This Because while loop was in a subshell
echo $i
# stdout: 0
## stdout: 0
### while in pipe with subshell
#### while in pipe with subshell
i=0
find . -maxdepth 1 -name INSTALL.txt -o -name LICENSE.txt | ( while read path; do
i=$((i+1))
#echo $i
done
echo $i )
# stdout: 2
## stdout: 2
### until loop
#### until loop
# This is just the opposite of while? while ! cond?
until false; do
echo hi
break
done
# stdout: hi
## stdout: hi
View
@@ -1,13 +1,13 @@
#!/usr/bin/env bash
### debug-line builtin
#### debug-line builtin
debug-line 'hi there'
# status: 0
## status: 0
### debug-completion option
#### debug-completion option
set -o debug-completion
# status: 0
## status: 0
### debug-completion from command line
#### debug-completion from command line
$SH -o debug-completion
# status: 0
## status: 0
View
@@ -1,123 +1,123 @@
#!/usr/bin/env bash
### Bad var sub
#### Bad var sub
echo $%
# stdout: $%
## stdout: $%
### Bad braced var sub -- not allowed
#### Bad braced var sub -- not allowed
echo ${%}
# status: 2
# OK bash/mksh status: 1
## status: 2
## OK bash/mksh status: 1
### Bad var sub caught at parse time
#### Bad var sub caught at parse time
if test -f /; then
echo ${%}
else
echo ok
fi
# status: 2
# BUG dash/bash/mksh status: 0
## status: 2
## BUG dash/bash/mksh status: 0
### Incomplete while
#### Incomplete while
echo hi; while
echo status=$?
# status: 2
# stdout-json: ""
# OK mksh status: 1
## status: 2
## stdout-json: ""
## OK mksh status: 1
### Incomplete for
#### Incomplete for
echo hi; for
echo status=$?
# status: 2
# stdout-json: ""
# OK mksh status: 1
## status: 2
## stdout-json: ""
## OK mksh status: 1
### Incomplete if
#### Incomplete if
echo hi; if
echo status=$?
# status: 2
# stdout-json: ""
# OK mksh status: 1
## status: 2
## stdout-json: ""
## OK mksh status: 1
### do unexpected
#### do unexpected
do echo hi
# status: 2
# stdout-json: ""
# OK mksh status: 1
## status: 2
## stdout-json: ""
## OK mksh status: 1
### } is a parse error
#### } is a parse error
}
echo should not get here
# stdout-json: ""
# status: 2
# OK mksh status: 1
## stdout-json: ""
## status: 2
## OK mksh status: 1
### { is its own word, needs a space
#### { is its own word, needs a space
# bash and mksh give parse time error because of }
# dash gives 127 as runtime error
{ls; }
echo "status=$?"
# stdout-json: ""
# status: 2
# OK mksh status: 1
# BUG dash stdout: status=127
# BUG dash status: 0
## stdout-json: ""
## status: 2
## OK mksh status: 1
## BUG dash stdout: status=127
## BUG dash status: 0
### } on the second line
#### } on the second line
set -o errexit
{ls;
}
# status: 127
## status: 127
### Invalid for loop variable name
#### Invalid for loop variable name
for i.j in a b c; do
echo hi
done
echo done
# stdout-json: ""
# status: 2
# OK mksh status: 1
# OK bash status: 0
# BUG bash stdout: done
## stdout-json: ""
## status: 2
## OK mksh status: 1
## OK bash status: 0
## BUG bash stdout: done
### bad var name globally isn't parsed like an assignment
#### bad var name globally isn't parsed like an assignment
# bash and dash disagree on exit code.
FOO-BAR=foo
# status: 127
## status: 127
### bad var name in export
#### bad var name in export
# bash and dash disagree on exit code.
export FOO-BAR=foo
# status: 2
# OK bash/mksh status: 1
## status: 2
## OK bash/mksh status: 1
### bad var name in local
#### bad var name in local
# bash and dash disagree on exit code.
f() {
local FOO-BAR=foo
}
# status: 2
# BUG dash/bash/mksh status: 0
## status: 2
## BUG dash/bash/mksh status: 0
### misplaced parentheses are not a subshell
#### misplaced parentheses are not a subshell
echo a(b)
# status: 2
# OK mksh status: 1
## status: 2
## OK mksh status: 1
### incomplete command sub
#### incomplete command sub
$(x
# status: 2
# OK mksh status: 1
## status: 2
## OK mksh status: 1
### misplaced ;;
#### misplaced ;;
echo 1 ;; echo 2
# stdout-json: ""
# status: 2
# OK mksh status: 1
## stdout-json: ""
## status: 2
## OK mksh status: 1
### empty clause in [[
#### empty clause in [[
# regression test for commit 451ca9e2b437e0326fc8155783d970a6f32729d8
[[ || true ]]
# status: 2
# N-I dash status: 0
# OK mksh status: 1
## status: 2
## N-I dash status: 0
## OK mksh status: 1
View
@@ -6,118 +6,118 @@
# pipeline : pipe_sequence
# | Bang pipe_sequence
### Brace group in pipeline
#### Brace group in pipeline
{ echo one; echo two; } | tac
# stdout-json: "two\none\n"
## stdout-json: "two\none\n"
### For loop starts pipeline
#### For loop starts pipeline
for w in one two; do
echo $w
done | tac
# stdout-json: "two\none\n"
## stdout-json: "two\none\n"
### While Loop ends pipeline
#### While Loop ends pipeline
seq 3 | while read i
do
echo ".$i"
done
# stdout-json: ".1\n.2\n.3\n"
## stdout-json: ".1\n.2\n.3\n"
### Redirect in Pipeline
#### Redirect in Pipeline
echo hi 1>&2 | wc -l
# stdout: 0
# BUG zsh stdout: 1
## stdout: 0
## BUG zsh stdout: 1
### Pipeline comments
#### Pipeline comments
echo abcd | # input
# blank line
tr a-z A-Z # transform
# stdout: ABCD
## stdout: ABCD
### Exit code is last status
#### Exit code is last status
echo a | egrep '[0-9]+'
# status: 1
## status: 1
### PIPESTATUS
#### PIPESTATUS
{ sleep 0.03; exit 1; } | { sleep 0.02; exit 2; } | { sleep 0.01; exit 3; }
echo ${PIPESTATUS[@]}
# stdout: 1 2 3
# N-I dash status: 2
# N-I zsh status: 3
# N-I dash/zsh stdout-json: ""
## stdout: 1 2 3
## N-I dash status: 2
## N-I zsh status: 3
## N-I dash/zsh stdout-json: ""
### |&
#### |&
stdout_stderr.py |& cat
# stdout-json: "STDERR\nSTDOUT\n"
# status: 0
# N-I dash/mksh stdout-json: ""
# N-I dash status: 2
## stdout-json: "STDERR\nSTDOUT\n"
## status: 0
## N-I dash/mksh stdout-json: ""
## N-I dash status: 2
### ! turns non-zero into zero
#### ! turns non-zero into zero
! $SH -c 'exit 42'; echo $?
# stdout: 0
# status: 0
## stdout: 0
## status: 0
### ! turns zero into 1
#### ! turns zero into 1
! $SH -c 'exit 0'; echo $?
# stdout: 1
# status: 0
## stdout: 1
## status: 0
### ! in if
#### ! in if
if ! echo hi; then
echo TRUE
else
echo FALSE
fi
# stdout-json: "hi\nFALSE\n"
# status: 0
## stdout-json: "hi\nFALSE\n"
## status: 0
### ! with ||
#### ! with ||
! echo hi || echo FAILED
# stdout-json: "hi\nFAILED\n"
# status: 0
## stdout-json: "hi\nFAILED\n"
## status: 0
### ! with { }
#### ! with { }
! { echo 1; echo 2; } || echo FAILED
# stdout-json: "1\n2\nFAILED\n"
# status: 0
## stdout-json: "1\n2\nFAILED\n"
## status: 0
### ! with ( )
#### ! with ( )
! ( echo 1; echo 2 ) || echo FAILED
# stdout-json: "1\n2\nFAILED\n"
# status: 0
## stdout-json: "1\n2\nFAILED\n"
## status: 0
### ! is not a command
#### ! is not a command
v='!'
$v echo hi
# status: 127
## status: 127
### Evaluation of argv[0] in pipeline occurs in child
#### Evaluation of argv[0] in pipeline occurs in child
${cmd=echo} hi | wc -l
echo "cmd=$cmd"
# stdout-json: "1\ncmd=\n"
# BUG zsh stdout-json: "1\ncmd=echo\n"
## stdout-json: "1\ncmd=\n"
## BUG zsh stdout-json: "1\ncmd=echo\n"
### last command is run in its own process
#### last command is run in its own process
echo hi | read line
echo "line=$line"
# stdout: line=
# OK zsh stdout: line=hi
## stdout: line=
## OK zsh stdout: line=hi
### shopt -s lastpipe
#### shopt -s lastpipe
shopt -s lastpipe
echo hi | read line
echo "line=$line"
# stdout: line=hi
# N-I dash/mksh stdout: line=
## stdout: line=hi
## N-I dash/mksh stdout: line=
### shopt -s lastpipe
#### shopt -s lastpipe
shopt -s lastpipe
i=0
seq 3 | while read line; do
(( i++ ))
done
echo i=$i
# stdout: i=3
# N-I dash/mksh stdout: i=0
## stdout: i=3
## N-I dash/mksh stdout: i=0
View
@@ -5,49 +5,49 @@
# My tests
### Empty for loop is allowed
#### Empty for loop is allowed
for x in; do
echo hi
echo $x
done
# stdout-json: ""
## stdout-json: ""
### Empty for loop without in. Do can be on the same line I guess.
#### Empty for loop without in. Do can be on the same line I guess.
for x do
echo hi
echo $x
done
# stdout-json: ""
## stdout-json: ""
### Empty case statement
#### Empty case statement
case foo in
esac
# stdout-json: ""
## stdout-json: ""
### Last case without ;;
#### Last case without ;;
foo=a
case $foo in
a) echo A ;;
b) echo B
esac
# stdout: A
## stdout: A
### Only case without ;;
#### Only case without ;;
foo=a
case $foo in
a) echo A
esac
# stdout: A
## stdout: A
### Case with optional (
#### Case with optional (
foo=a
case $foo in
(a) echo A ;;
(b) echo B
esac
# stdout: A
## stdout: A
### Empty action for case is syntax error
#### Empty action for case is syntax error
# POSIX grammar seems to allow this, but bash and dash don't. Need ;;
foo=a
case $foo in
@@ -56,31 +56,31 @@ case $foo in
echo A ;;
d)
esac
# status: 2
# OK mksh status: 1
## status: 2
## OK mksh status: 1
### Empty action is allowed for last case
#### Empty action is allowed for last case
foo=b
case $foo in
a) echo A ;;
b)
esac
# stdout-json: ""
## stdout-json: ""
### Case with | pattern
#### Case with | pattern
foo=a
case $foo in
a|b) echo A ;;
c)
esac
# stdout: A
## stdout: A
### Bare semi-colon not allowed
#### Bare semi-colon not allowed
# This is disallowed by the grammar; bash and dash don't accept it.
;
# status: 2
# OK mksh status: 1
## status: 2
## OK mksh status: 1
@@ -90,21 +90,21 @@ esac
### Command substitution in default
#### Command substitution in default
echo ${x:-$(ls -d /bin)}
# stdout: /bin
## stdout: /bin
### Arithmetic expansion
#### Arithmetic expansion
x=3
while [ $x -gt 0 ]
do
echo $x
x=$(($x-1))
done
# stdout-json: "3\n2\n1\n"
## stdout-json: "3\n2\n1\n"
### Newlines in compound lists
#### Newlines in compound lists
x=3
while
# a couple of <newline>s
@@ -124,21 +124,21 @@ do
[ $x -eq 0 ] && break
done
# Not testing anything but the status since output is complicated
# status: 0
## status: 0
### Multiple here docs on one line
#### Multiple here docs on one line
cat <<EOF1; cat <<EOF2
one
EOF1
two
EOF2
# stdout-json: "one\ntwo\n"
## stdout-json: "one\ntwo\n"
### cat here doc; echo; cat here doc
#### cat here doc; echo; cat here doc
cat <<EOF1; echo two; cat <<EOF2
one
EOF1
three
EOF2
# stdout-json: "one\ntwo\nthree\n"
## stdout-json: "one\ntwo\nthree\n"
View
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
### Process sub input
#### Process sub input
f=_tmp/process-sub.txt
{ echo 1; echo 2; echo 3; } > $f
cat <(head -n 2 $f) <(tail -n 2 $f)
@@ -11,15 +11,15 @@ cat <(head -n 2 $f) <(tail -n 2 $f)
3
## END
### Process sub output
#### Process sub output
{ echo 1; echo 2; echo 3; } > >(tac)
## STDOUT:
3
2
1
## END
### Non-linear pipeline with >()
#### Non-linear pipeline with >()
stdout_stderr() {
echo o1
echo o2
Oops, something went wrong.