1 #!/usr/bin/env bash
2
3 #### -A function prints functions
4 add () { expr 4 + 4; }
5 div () { expr 6 / 2; }
6 ek () { echo hello; }
7 __ec () { echo hi; }
8 _ab () { expr 10 % 3; }
9 compgen -A function
10 echo --
11 compgen -A function _
12 ## status: 0
13 ## STDOUT:
14 __ec
15 _ab
16 add
17 div
18 ek
19 --
20 __ec
21 _ab
22 ## END
23
24 #### Invalid syntax
25 compgen -A foo
26 echo status=$?
27 ## stdout: status=2
28
29 #### how compgen calls completion functions
30 foo_complete() {
31 # first, cur, prev
32 argv.py argv "$@"
33 argv.py COMP_WORDS "${COMP_WORDS[@]}"
34 argv.py COMP_CWORD "${COMP_CWORD}"
35 argv.py COMP_LINE "${COMP_LINE}"
36 argv.py COMP_POINT "${COMP_POINT}"
37 #return 124
38 COMPREPLY=(one two three)
39 }
40 compgen -F foo_complete foo a b c
41 ## STDOUT:
42 ['argv', 'compgen', 'foo', '']
43 ['COMP_WORDS']
44 ['COMP_CWORD', '-1']
45 ['COMP_LINE', '']
46 ['COMP_POINT', '0']
47 one
48 two
49 three
50 ## END
51
52 #### complete -o -F (git)
53 foo() { echo foo; }
54 wrapper=foo
55 complete -o default -o nospace -F $wrapper git
56 ## status: 0
57
58 #### compopt with invalid syntax
59 compopt -o invalid
60 echo status=$?
61 ## stdout: status=2
62
63 #### compopt fails when not in completion function
64 # NOTE: Have to be executing a completion function
65 compopt -o filenames +o nospace
66 ## status: 1
67
68 #### compgen -f on invalid dir
69 compgen -f /non-existing-dir/
70 ## status: 1
71 ## stdout-json: ""
72
73 #### compgen -f
74 mkdir -p $TMP/compgen
75 touch $TMP/compgen/{one,two,three}
76 cd $TMP/compgen
77 compgen -f | sort
78 echo --
79 compgen -f t | sort
80 ## STDOUT:
81 one
82 three
83 two
84 --
85 three
86 two
87 ## END
88
89 #### compgen -v with local vars
90 v1_global=0
91 f() {
92 local v2_local=0
93 compgen -v v
94 }
95 f
96 ## STDOUT:
97 v1_global
98 v2_local
99 ## END
100
101 #### compgen -v on unknown var
102 compgen -v __nonexistent__
103 ## status: 1
104 ## stdout-json: ""
105
106 #### compgen -v P
107 cd > /dev/null # for some reason in bash, this makes PIPESTATUS appear!
108 compgen -v P | grep -E '^PATH|PWD' | sort
109 ## STDOUT:
110 PATH
111 PWD
112 ## END
113
114 #### compgen with actions: function / variable / file
115 mkdir -p $TMP/compgen2
116 touch $TMP/compgen2/PA_FILE_{1,2}
117 cd $TMP/compgen2 # depends on previous test above!
118 PA_FUNC() { echo P; }
119 Q_FUNC() { echo Q; }
120 compgen -A function -A variable -A file PA
121 ## STDOUT:
122 PA_FUNC
123 PATH
124 PA_FILE_1
125 PA_FILE_2
126 ## END
127
128 #### compgen with actions: alias, setopt
129 alias v_alias='ls'
130 alias v_alias2='ls'
131 alias a1='ls'
132 compgen -A alias -A setopt v
133 ## STDOUT:
134 v_alias
135 v_alias2
136 verbose
137 vi
138 ## END
139
140 #### compgen with actions: shopt
141 compgen -A shopt -P [ -S ] nu
142 ## STDOUT:
143 [nullglob]
144 ## END
145
146 #### compgen with action and suffix: helptopic
147 compgen -A helptopic -S ___ fa
148 ## STDOUT:
149 false___
150 ## END
151
152 #### compgen -A directory
153 # omit portable-files.mk
154 compgen -A directory p | sort
155 ## STDOUT:
156 pgen2
157 pylib
158 py-yajl
159 ## END
160
161 #### compgen -A file
162 compgen -A file b | sort
163 ## STDOUT:
164 benchmarks
165 bin
166 build
167 ## END
168
169 #### compgen -A user
170 # no assertion because this isn't hermetic
171 compgen -A user
172 ## status: 0
173
174 #### compgen -A command completes external commands
175 # NOTE: this test isn't hermetic
176 compgen -A command xarg | uniq
177 echo status=$?
178 ## STDOUT:
179 xargs
180 status=0
181 ## END
182
183 #### compgen -A command completes functions and aliases
184 my_func() { echo ; }
185 my_func2() { echo ; }
186 alias my_alias=foo
187 compgen -A command my_
188 echo status=$?
189 ## STDOUT:
190 my_alias
191 my_func
192 my_func2
193 status=0
194 ## END
195
196 #### compgen -A command completes builtins and keywords
197 compgen -A command eva
198 echo status=$?
199 compgen -A command whil
200 echo status=$?
201 ## STDOUT:
202 eval
203 status=0
204 while
205 status=0
206 ## END
207
208 #### complete with nonexistent function
209 complete -F invalidZZ -D
210 echo status=$?
211 ## stdout: status=2
212 ## BUG bash stdout: status=0
213
214 #### complete with no action
215 complete foo
216 echo status=$?
217 ## stdout: status=2
218 ## BUG bash stdout: status=0
219
220 #### -o filenames and -o nospace have no effect with compgen
221 # they are POSTPROCESSING.
222 compgen -o filenames -o nospace -W 'bin build'
223 ## STDOUT:
224 bin
225 build
226 ## END
227
228 #### -o plusdirs and -o dirnames with compgen
229 compgen -o plusdirs -W 'a b1 b2' b | sort
230 echo ---
231 compgen -o dirnames b | sort
232 ## STDOUT:
233 b1
234 b2
235 benchmarks
236 bin
237 build
238 ---
239 benchmarks
240 bin
241 build
242 ## END
243
244 #### compgen -o default completes files and dirs
245 compgen -o default spec/t | sort
246 ## STDOUT:
247 spec/testdata
248 spec/tilde.test.sh
249 spec/type-compat.test.sh
250 ## END
251
252 #### compgen doesn't respect -X for user-defined functions
253 # WORKAROUND: wrap in bash -i -c because non-interactive bash behaves
254 # differently!
255 case $SH in
256 *bash|*osh)
257 $SH --rcfile /dev/null -i -c '
258 shopt -s extglob
259 fun() {
260 COMPREPLY=(one two three bin)
261 }
262 compgen -X "@(two|bin)" -F fun
263 echo --
264 compgen -X "!@(two|bin)" -F fun
265 '
266 esac
267 ## STDOUT:
268 one
269 three
270 --
271 two
272 bin
273 ## END
274
275 #### compgen -W words -X filter
276 # WORKAROUND: wrap in bash -i -c because non-interactive bash behaves
277 # differently!
278 case $SH in
279 *bash|*osh)
280 $SH --rcfile /dev/null -i -c 'shopt -s extglob; compgen -X "@(two|bin)" -W "one two three bin"'
281 esac
282 ## STDOUT:
283 one
284 three
285 ## END
286
287 #### compgen -f -X filter -- $cur
288 cd $TMP
289 touch spam.py spam.sh
290 compgen -f -- sp | sort
291 echo --
292 # WORKAROUND: wrap in bash -i -c because non-interactive bash behaves
293 # differently!
294 case $SH in
295 *bash|*osh)
296 $SH --rcfile /dev/null -i -c 'shopt -s extglob; compgen -f -X "!*.@(py)" -- sp'
297 esac
298 ## STDOUT:
299 spam.py
300 spam.sh
301 --
302 spam.py
303 ## END
304
305 #### compgen doesn't need shell quoting
306 # There is an obsolete comment in bash_completion that claims the opposite.
307 cd $TMP
308 touch 'foo bar'
309 touch "foo'bar"
310 compgen -f "foo b"
311 compgen -f "foo'"
312 ## STDOUT:
313 foo bar
314 foo'bar
315 ## END
316
317 #### compgen -W 'one two three'
318 compgen -W 'one two three'
319 echo --
320 compgen -W 'w1 w2 three' -A directory w
321 echo --
322 compgen -A directory -W 'w1 w2 three' w # order doesn't matter
323 ## STDOUT:
324 one
325 two
326 three
327 --
328 web
329 w1
330 w2
331 --
332 web
333 w1
334 w2
335 ## END
336
337 #### compgen -W evaluates code in $()
338 IFS=':%'
339 compgen -W '$(echo "spam:eggs%ham cheese")'
340 ## STDOUT:
341 spam
342 eggs
343 ham cheese
344 ## END
345
346 #### compgen -W uses IFS, and delimiters are escaped with \
347 IFS=':%'
348 compgen -W 'spam:eggs%ham cheese\:colon'
349 ## STDOUT:
350 spam
351 eggs
352 ham cheese:colon
353 ## END
354
355 #### Parse errors for compgen -W and complete -W
356 # bash doesn't detect as many errors because it lacks static parsing.
357 compgen -W '${'
358 echo status=$?
359 complete -W '${' foo
360 echo status=$?
361 ## STDOUT:
362 status=2
363 status=2
364 ## END
365 ## BUG bash STDOUT:
366 status=1
367 status=0
368 ## END
369
370 #### Runtime errors for compgen -W
371 compgen -W 'foo $(( 1 / 0 )) bar'
372 echo status=$?
373 ## STDOUT:
374 status=1
375 ## END
376
377 #### Runtime errors for compgen -F func
378 _foo() {
379 COMPREPLY=( foo bar )
380 COMPREPLY+=( $(( 1 / 0 )) ) # FATAL, but we still have candidates
381 }
382 compgen -F _foo foo
383 echo status=$?
384 ## STDOUT:
385 status=1
386 ## END
387
388 #### compgen -W '' cmd is not a usage error
389 # Bug fix due to '' being falsey in Python
390 compgen -W '' -- foo
391 echo status=$?
392 ## stdout: status=1