1 #!/usr/bin/env bash
2 #
3 # Alias is in POSIX.
4 #
5 # http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_03_01
6 #
7 # Bash is the only one that doesn't support aliases by default!
8
9 #### Usage of builtins
10 shopt -s expand_aliases || true
11 alias -- foo=echo
12 echo status=$?
13 foo x
14 unalias -- foo
15 foo x
16 ## status: 127
17 ## STDOUT:
18 status=0
19 x
20 ## END
21 # dash doesn't accept --
22 ## BUG dash STDOUT:
23 status=1
24 x
25 ## END
26
27 #### Basic alias
28 shopt -s expand_aliases # bash requires this
29 alias hi='echo hello world'
30 hi || echo 'should not run this'
31 echo hi # second word is not
32 'hi' || echo 'expected failure'
33 ## STDOUT:
34 hello world
35 hi
36 expected failure
37 ## END
38
39 #### define and use alias on a single line
40 shopt -s expand_aliases
41 alias e=echo; e one # this is not alias-expanded because we parse lines at once
42 e two; e three
43 ## STDOUT:
44 two
45 three
46 ## END
47
48 #### alias can override builtin
49 shopt -s expand_aliases
50 alias echo='echo foo'
51 echo bar
52 ## stdout: foo bar
53
54 #### defining multiple aliases, then unalias
55 shopt -s expand_aliases # bash requires this
56 x=x
57 y=y
58 alias echo-x='echo $x' echo-y='echo $y'
59 echo status=$?
60 echo-x X
61 echo-y Y
62 unalias echo-x echo-y
63 echo status=$?
64 echo-x X || echo undefined
65 echo-y Y || echo undefined
66 ## STDOUT:
67 status=0
68 x X
69 y Y
70 status=0
71 undefined
72 undefined
73 ## END
74
75 #### alias not defined
76 alias e='echo' nonexistentZ
77 echo status=$?
78 ## STDOUT:
79 status=1
80 ## END
81 ## OK mksh STDOUT:
82 nonexistentZ alias not found
83 status=1
84 ## END
85
86 #### unalias not defined
87 alias e=echo ll='ls -l'
88 unalias e nonexistentZ ll
89 echo status=$?
90 ## STDOUT:
91 status=1
92 ## END
93
94 #### listing given aliases
95 alias e=echo ll='ls -l'
96 alias e ll
97 ## STDOUT:
98 alias e='echo'
99 alias ll='ls -l'
100 ## END
101 ## OK mksh/zsh STDOUT:
102 e=echo
103 ll='ls -l'
104 ## END
105 ## OK dash STDOUT:
106 e='echo'
107 ll='ls -l'
108 ## END
109
110 #### alias without args lists all aliases
111 alias ex=exit ll='ls -l'
112 alias | grep -E 'ex=|ll=' # need to grep because mksh/zsh have builtin aliases
113 echo status=$?
114 ## STDOUT:
115 alias ex='exit'
116 alias ll='ls -l'
117 status=0
118 ## END
119 ## OK dash STDOUT:
120 ex='exit'
121 ll='ls -l'
122 status=0
123 ## END
124 ## OK mksh/zsh STDOUT:
125 ex=exit
126 ll='ls -l'
127 status=0
128 ## END
129
130 #### unalias without args is a usage error
131 unalias
132 echo status=$?
133 ## stdout: status=2
134 ## BUG mksh/dash stdout: status=0
135 ## BUG zsh stdout: status=1
136
137 #### alias with trailing space causes alias expansion on second word
138 shopt -s expand_aliases # bash requires this
139
140 alias hi='echo hello world '
141 alias punct='!!!'
142
143 hi punct
144
145 alias hi='echo hello world' # No trailing space
146
147 hi punct
148
149 ## STDOUT:
150 hello world !!!
151 hello world punct
152 ## END
153
154 #### Recursive alias expansion of first word
155 shopt -s expand_aliases # bash requires this
156 alias hi='e_ hello world'
157 alias e_='echo __'
158 hi # first hi is expanded to echo hello world; then echo is expanded. gah.
159 ## STDOUT:
160 __ hello world
161 ## END
162
163 #### Recursive alias expansion of SECOND word
164 shopt -s expand_aliases # bash requires this
165 alias one='ONE '
166 alias two='TWO '
167 alias e_='echo one '
168 e_ two hello world
169 ## STDOUT:
170 one TWO hello world
171 ## END
172
173 #### Expansion of alias with variable
174 shopt -s expand_aliases # bash requires this
175 x=x
176 alias echo-x='echo $x' # nothing is evaluated here
177 x=y
178 echo-x hi
179 ## STDOUT:
180 y hi
181 ## END
182
183 #### Alias must be an unquoted word, no expansions allowed
184 shopt -s expand_aliases # bash requires this
185 alias echo_alias_='echo'
186 cmd=echo_alias_
187 echo_alias_ X # this works
188 $cmd X # this fails because it's quoted
189 echo status=$?
190 ## STDOUT:
191 X
192 status=127
193 ## END
194
195 #### first and second word are the same alias, but no trailing space
196 shopt -s expand_aliases # bash requires this
197 x=x
198 alias echo-x='echo $x' # nothing is evaluated here
199 echo-x echo-x
200 ## STDOUT:
201 x echo-x
202 ## END
203
204 #### first and second word are the same alias, with trailing space
205 shopt -s expand_aliases # bash requires this
206 x=x
207 alias echo-x='echo $x ' # nothing is evaluated here
208 echo-x echo-x
209 ## STDOUT:
210 x echo x
211 ## END
212
213 #### Invalid syntax of alias
214 shopt -s expand_aliases # bash requires this
215 alias echo_alias_= 'echo --; echo' # bad space here
216 echo_alias_ x
217 ## status: 127
218
219 #### Dynamic alias definition
220 shopt -s expand_aliases # bash requires this
221 x=x
222 name='echo_alias_'
223 val='=echo'
224 alias "$name$val"
225 echo_alias_ X
226 ## stdout: X
227
228 #### Alias name with punctuation
229 # NOTE: / is not OK in bash, but OK in other shells. Must less restrictive
230 # than var names.
231 shopt -s expand_aliases # bash requires this
232 alias e_+.~x='echo'
233 e_+.~x X
234 ## stdout: X
235
236 #### Syntax error after expansion
237 shopt -s expand_aliases # bash requires this
238 alias e_=';; oops'
239 e_ x
240 ## status: 2
241 ## OK mksh/zsh status: 1
242
243 #### Loop split across alias and arg works
244 shopt -s expand_aliases # bash requires this
245 alias e_='for i in 1 2 3; do echo $i;'
246 e_ done
247 ## STDOUT:
248 1
249 2
250 3
251 ## END
252
253 #### Loop split across alias in another way
254 shopt -s expand_aliases
255 alias e_='for i in 1 2 3; do echo '
256 e_ $i; done
257 ## STDOUT:
258 1
259 2
260 3
261 ## END
262 ## OK osh stdout-json: ""
263 ## OK osh status: 2
264
265 #### Loop split across both iterative and recursive aliases
266 shopt -s expand_aliases # bash requires this
267 alias FOR1='for '
268 alias FOR2='FOR1 '
269 alias eye1='i '
270 alias eye2='eye1 '
271 alias IN='in '
272 alias onetwo='$one "2" ' # NOTE: this does NOT work in any shell except bash.
273 one=1
274 FOR2 eye2 IN onetwo 3; do echo $i; done
275 ## STDOUT:
276 1
277 2
278 3
279 ## END
280 ## OK osh stdout-json: ""
281 ## OK osh status: 2
282 ## BUG zsh stdout-json: ""
283
284 #### Alias with a quote in the middle is a syntax error
285 shopt -s expand_aliases
286 alias e_='echo "'
287 var=x
288 e_ '${var}"'
289 ## status: 2
290 ## OK mksh/zsh status: 1
291
292 #### Alias with internal newlines
293 shopt -s expand_aliases
294 alias e_='echo 1
295 echo 2
296 echo 3'
297 var='echo foo'
298 e_ ${var}
299 ## STDOUT:
300 1
301 2
302 3 echo foo
303 ## END
304
305 #### Alias trailing newline
306 shopt -s expand_aliases
307 alias e_='echo 1
308 echo 2
309 echo 3
310 '
311 var='echo foo'
312 e_ ${var}
313 ## STDOUT:
314 1
315 2
316 3
317 foo
318 ## END
319 ## OK zsh STDOUT:
320 1
321 2
322 3
323 ## END
324 ## OK zsh status: 127
325
326 #### Two aliases in pipeline
327 shopt -s expand_aliases
328 alias SEQ='seq '
329 alias THREE='3 '
330 alias WC='wc '
331 SEQ THREE | WC -l
332 ## stdout: 3
333
334 #### Alias not respected inside $()
335 # This could be parsed correctly, but it is only defined in a child process.
336 shopt -s expand_aliases
337 echo $(alias sayhi='echo hello')
338 sayhi
339 ## status: 127
340
341 #### Alias can be defined and used on a single line
342 shopt -s expand_aliases
343 alias sayhi='echo hello'; sayhi same line
344 sayhi other line
345 ## STDOUT:
346 hello other line
347 ## END
348
349 #### Alias is respected inside eval
350 shopt -s expand_aliases
351 eval "alias sayhi='echo hello'
352 sayhi inside"
353 sayhi outside
354 ## STDOUT:
355 hello inside
356 hello outside
357 ## END
358 ## BUG zsh STDOUT:
359 hello outside
360 ## END
361
362 #### alias with redirects works
363 shopt -s expand_aliases
364 alias e_=echo
365 >$TMP/alias1.txt e_ 1
366 e_ >$TMP/alias2.txt 2
367 e_ 3 >$TMP/alias3.txt
368 cat $TMP/alias1.txt $TMP/alias2.txt $TMP/alias3.txt
369 ## STDOUT:
370 1
371 2
372 3
373 ## END
374
375 #### alias with environment bindings works
376 shopt -s expand_aliases
377 alias p_=printenv.py
378 FOO=1 printenv.py FOO
379 FOO=2 p_ FOO
380 ## STDOUT:
381 1
382 2
383 ## END
384
385 #### alias with line continuation in the middle
386 shopt -s expand_aliases
387 alias e_='echo '
388 alias one='ONE '
389 alias two='TWO '
390 alias three='THREE' # no trailing space
391 e_ one \
392 two one \
393 two three two \
394 one
395 ## stdout: ONE TWO ONE TWO THREE two one
396
397 #### alias for left brace
398 shopt -s expand_aliases
399 alias LEFT='{'
400 LEFT echo one; echo two; }
401 ## STDOUT:
402 one
403 two
404 ## END
405 ## OK osh stdout-json: ""
406 ## OK osh status: 2
407
408 #### alias for left paren
409 shopt -s expand_aliases
410 alias LEFT='('
411 LEFT echo one; echo two )
412 ## STDOUT:
413 one
414 two
415 ## END
416 ## OK osh stdout-json: ""
417 ## OK osh status: 2
418
419 #### alias used in subshell and command sub
420 # This spec seems to be contradictoary?
421 # http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_03_01
422 # "When used as specified by this volume of POSIX.1-2017, alias definitions
423 # shall not be inherited by separate invocations of the shell or by the utility
424 # execution environments invoked by the shell; see Shell Execution
425 # Environment."
426 shopt -s expand_aliases
427 alias echo_='echo [ '
428 ( echo_ subshell; )
429 echo $(echo_ commandsub)
430 ## STDOUT:
431 [ subshell
432 [ commandsub
433 ## END
434
435 #### alias used in here doc
436 shopt -s expand_aliases
437 alias echo_='echo [ '
438 cat <<EOF
439 $(echo_ ])
440 EOF
441 ## STDOUT:
442 [ ]
443 ## END
444
445 #### here doc inside alias
446 shopt -s expand_aliases
447 alias c='cat <<EOF
448 $(echo hi)
449 EOF
450 '
451 c
452 ## STDOUT:
453 hi
454 ## END
455 ## BUG bash stdout-json: ""
456 ## BUG bash status: 127
457
458 #### Corner case: alias inside LHS array arithmetic expression
459 shopt -s expand_aliases
460 alias zero='echo 0'
461 a[$(zero)]=ZERO
462 a[1]=ONE
463 argv.py "${a[@]}"
464 ## STDOUT:
465 ['ZERO', 'ONE']
466 ## END
467 ## N-I dash stdout-json: ""
468 ## N-I dash status: 2
469 ## N-I zsh stdout-json: ""
470 ## N-I zsh status: 1
471
472 #### Alias that is pipeline
473 shopt -s expand_aliases
474 alias t1='echo hi|wc -c'
475 t1
476 ## STDOUT:
477 3
478 ## END
479
480 #### Alias that is && || ;
481 shopt -s expand_aliases
482 alias t1='echo one && echo two && echo 3 | wc -l;
483 echo four'
484 t1
485 ## STDOUT:
486 one
487 two
488 1
489 four
490 ## END
491
492 #### Alias and command sub (bug regression)
493 cd $TMP
494 shopt -s expand_aliases
495 echo foo bar > tmp.txt
496 alias a=argv.py
497 a `cat tmp.txt`
498 ## stdout: ['foo', 'bar']
499
500 #### Alias and arithmetic
501 shopt -s expand_aliases
502 alias a=argv.py
503 a $((1 + 2))
504 ## stdout: ['3']
505
506 #### Alias and PS4
507 # dash enters an infinite loop!
508 case $SH in
509 dash)
510 exit 1
511 ;;
512 esac
513
514 set -x
515 PS4='+$(echo trace) '
516 shopt -s expand_aliases
517 alias a=argv.py
518 a foo bar
519 ## stdout: ['foo', 'bar']
520 ## BUG dash status: 1
521 ## BUG dash stdout-json: ""
522
523 #### alias with keywords
524 # from issue #299
525 shopt -s expand_aliases
526 alias a=
527
528 # both of these fail to parse in OSH
529 # this is because of our cleaner evaluation model
530
531 a (( var = 0 ))
532 #a case x in x) true;; esac
533
534 echo done
535 ## stdout: done
536 ## OK osh status: 2
537 ## OK osh stdout-json: ""