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