Permalink
Browse files

Fill out spec tests for extended glob.

It is used in a number of different projects, including
bash-completions, so I want to at least parse it now.
  • Loading branch information...
Andy Chu
Andy Chu committed Oct 18, 2017
1 parent 08622d4 commit fba1a2e4a825ad533d4c87c641cbc5aabbb0e52e
Showing with 147 additions and 9 deletions.
  1. +3 −1 osh/osh.asdl
  2. +144 −8 spec/extended-glob.test.sh
View
@@ -85,6 +85,8 @@ module osh
| BracedIntRangePart(int start, int end, int? step)
-- {a..f} or {a..f..2} or {a..f..-2}
| BracedCharRangePart(string start, string end, int? step)
-- extended globs are parsed statically, unlike globs
| ExtGlobPart(id op_id, word* arms)
word =
TokenWord(token token)
@@ -98,7 +100,7 @@ module osh
| BracedWordTree(word_part* parts)
-- For dynamic parsing of test/[ -- the string is already evaluated.
| StringWord(id id, string s)
-- TODO: Might want to preserve token here.
lhs_expr =
LhsName(string name)
View
@@ -1,9 +1,14 @@
#!/usr/bin/env bash
#
# This is an OPTION in bash, but not mksh (because the feature originated in
# ksh). So it's probably low priority.
# ksh).
#
# It's also now a GNU libc extension to glob() and fnmatch().
# However all extended globs are syntax errors if shopt -s extglob isn't set,
# so I'm not sure what the point of having the option is. We should always
# parse it?
#
# It's also now a GNU libc extension to glob() and fnmatch() with FNM_EXTMATCH.
# However, this came after shells! Not sure who uses it. Does musl have it?
#
# I guess this is handy because it's like *.[ch]... but the extensions can be
# different length.
@@ -28,15 +33,146 @@ echo _tmp/@(*.cc|*.h)
# stdout: _tmp/bar.cc _tmp/bar.h _tmp/baz.h _tmp/foo.cc _tmp/foo.h
### ?() extended glob
# how is this different than the above?
# 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
### !() extended glob
# Hm this syntax isn't right?
### *() 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
### !(*.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
### @ matches exactly one
[[ --verbose == --@(help|verbose) ]] && echo TRUE
[[ --oops == --@(help|verbose) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
### ? matches 0 or 1
[[ -- == --?(help|verbose) ]] && echo TRUE
[[ --oops == --?(help|verbose) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
### + matches 1 or more
[[ --helphelp == --+(help|verbose) ]] && echo TRUE
[[ -- == --+(help|verbose) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
### * matches 0 or more
[[ -- == --*(help|verbose) ]] && echo TRUE
[[ --oops == --*(help|verbose) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
### simple repetition with *(foo) and +(Foo)
[[ foofoo == *(foo) ]] && echo TRUE
[[ foofoo == +(foo) ]] && echo TRUE
# stdout-json: "TRUE\nTRUE\n"
### ! matches none
[[ --oops == --!(help|verbose) ]] && echo TRUE
[[ --help == --!(help|verbose) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
### @() with variable arms
choice1='help'
choice2='verbose'
[[ --verbose == --@($choice1|$choice2) ]] && echo TRUE
[[ --oops == --@($choice1|$choice2) ]] || echo FALSE
# stdout-json: "TRUE\nFALSE\n"
### match is anchored
[[ foo_ == @(foo) ]] || echo FALSE
[[ _foo == @(foo) ]] || echo FALSE
[[ foo == @(foo) ]] && echo TRUE
# stdout-json: "FALSE\nFALSE\nTRUE\n"
### repeated match is anchored
[[ foofoo_ == +(foo) ]] || echo FALSE
[[ _foofoo == +(foo) ]] || echo FALSE
[[ foofoo == +(foo) ]] && echo TRUE
# stdout-json: "FALSE\nFALSE\nTRUE\n"
### 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"
### 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"
### adjacent extglob
[[ --help == @(--|++)@(help|verbose) ]] && echo TRUE
[[ ++verbose == @(--|++)@(help|verbose) ]] && echo TRUE
# stdout-json: "TRUE\nTRUE\n"
### 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"
### extglob in variable
shopt -s extglob
touch _tmp/{a,b}.R _tmp/{a,b}_test.R
echo */*.R!(*_test.R)
# stdout: _tmp/a.R _tmp/b.R
g=--@(help|verbose)
quoted='--@(help|verbose)'
[[ --help == $g ]] && echo TRUE
[[ --verbose == $g ]] && echo TRUE
[[ -- == $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"
### 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)
### case with extglob
shopt -s extglob
for word in --help --verbose --unmatched -- -zxzx -; do
case $word in
--@(help|verbose) )
echo A
continue
;;
( --?(b|c) )
echo B
continue
;;
( -+(x|z) )
echo C
continue
;;
( -*(x|z) )
echo D
continue
;;
*)
echo U
continue
;;
esac
done
# stdout-json: "A\nA\nU\nB\nC\nD\n"

0 comments on commit fba1a2e

Please sign in to comment.