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
186 #### first and second word are the same alias, with trailing space
187 shopt -s expand_aliases # bash requires this
188 x=x
189 alias echo-x='echo $x ' # nothing is evaluated here
190 echo-x echo-x
191 ## STDOUT:
192 x echo x
193 ## END
194
195 #### Invalid syntax of alias
196 shopt -s expand_aliases # bash requires this
197 alias echo_alias_= 'echo --; echo' # bad space here
198 echo_alias_ x
199 ## status: 127
200
201 #### Dynamic alias definition
202 shopt -s expand_aliases # bash requires this
203 x=x
204 name='echo_alias_'
205 val='=echo'
206 alias "$name$val"
207 echo_alias_ X
208 ## stdout: X
209
210 #### Alias name with punctuation
211 # NOTE: / is not OK in bash, but OK in other shells. Must less restrictive
212 # than var names.
213 shopt -s expand_aliases # bash requires this
214 alias e_+.~x='echo'
215 e_+.~x X
216 ## stdout: X
217
218 #### Syntax error after expansion
219 shopt -s expand_aliases # bash requires this
220 alias e_=';; oops'
221 e_ x
222 ## status: 2
223 ## OK mksh/zsh status: 1
224
225 #### Loop split across alias and arg works
226 shopt -s expand_aliases # bash requires this
227 alias e_='for i in 1 2 3; do echo $i;'
228 e_ done
229 ## STDOUT:
230 1
231 2
232 3
233 ## END
234
235 #### Loop split across alias in another way
236 shopt -s expand_aliases
237 alias e_='for i in 1 2 3; do echo '
238 e_ $i; done
239 ## STDOUT:
240 1
241 2
242 3
243 ## END
244 ## OK osh stdout-json: ""
245 ## OK osh status: 2
246
247 #### Loop split across both iterative and recursive aliases
248 shopt -s expand_aliases # bash requires this
249 alias FOR1='for '
250 alias FOR2='FOR1 '
251 alias eye1='i '
252 alias eye2='eye1 '
253 alias IN='in '
254 alias onetwo='$one "2" ' # NOTE: this does NOT work in any shell except bash.
255 one=1
256 FOR2 eye2 IN onetwo 3; do echo $i; done
257 ## STDOUT:
258 1
259 2
260 3
261 ## END
262 ## OK osh stdout-json: ""
263 ## OK osh status: 2
264 ## BUG zsh stdout-json: ""
265
266 #### Alias with a quote in the middle is a syntax error
267 shopt -s expand_aliases
268 alias e_='echo "'
269 var=x
270 e_ '${var}"'
271 ## status: 2
272 ## OK mksh/zsh status: 1
273
274 #### Alias with internal newlines
275 shopt -s expand_aliases
276 alias e_='echo 1
277 echo 2
278 echo 3'
279 var='echo foo'
280 e_ ${var}
281 ## STDOUT:
282 1
283 2
284 3 echo foo
285 ## END
286
287 #### Alias trailing newline
288 shopt -s expand_aliases
289 alias e_='echo 1
290 echo 2
291 echo 3
292 '
293 var='echo foo'
294 e_ ${var}
295 ## STDOUT:
296 1
297 2
298 3
299 foo
300 ## END
301 ## OK zsh STDOUT:
302 1
303 2
304 3
305 ## END
306 ## OK zsh status: 127
307
308 #### Two aliases in pipeline
309 shopt -s expand_aliases
310 alias SEQ='seq '
311 alias THREE='3 '
312 alias WC='wc '
313 SEQ THREE | WC -l
314 ## stdout: 3
315
316 #### Alias not respected inside $()
317 # This could be parsed correctly, but it is only defined in a child process.
318 shopt -s expand_aliases
319 echo $(alias sayhi='echo hello')
320 sayhi
321 ## status: 127
322
323 #### Alias can be defined and used on a single line
324 shopt -s expand_aliases
325 alias sayhi='echo hello'; sayhi same line
326 sayhi other line
327 ## STDOUT:
328 hello other line
329 ## END
330
331 #### Alias is respected inside eval
332 shopt -s expand_aliases
333 eval "alias sayhi='echo hello'
334 sayhi inside"
335 sayhi outside
336 ## STDOUT:
337 hello inside
338 hello outside
339 ## END
340 ## BUG zsh STDOUT:
341 hello outside
342 ## END
343
344 #### alias with redirects works
345 shopt -s expand_aliases
346 alias e_=echo
347 >$TMP/alias1.txt e_ 1
348 e_ >$TMP/alias2.txt 2
349 e_ 3 >$TMP/alias3.txt
350 cat $TMP/alias1.txt $TMP/alias2.txt $TMP/alias3.txt
351 ## STDOUT:
352 1
353 2
354 3
355 ## END
356
357 #### alias with environment bindings works
358 shopt -s expand_aliases
359 alias p_=printenv.py
360 FOO=1 printenv.py FOO
361 FOO=2 p_ FOO
362 ## STDOUT:
363 1
364 2
365 ## END
366
367 #### alias with line continuation in the middle
368 shopt -s expand_aliases
369 alias e_='echo '
370 alias one='ONE '
371 alias two='TWO '
372 alias three='THREE' # no trailing space
373 e_ one \
374 two one \
375 two three two \
376 one
377 ## stdout: ONE TWO ONE TWO THREE two one
378
379 #### alias for left brace
380 shopt -s expand_aliases
381 alias LEFT='{'
382 LEFT echo one; echo two; }
383 ## STDOUT:
384 one
385 two
386 ## END
387 ## OK osh stdout-json: ""
388 ## OK osh status: 2
389
390 #### alias for left paren
391 shopt -s expand_aliases
392 alias LEFT='('
393 LEFT echo one; echo two )
394 ## STDOUT:
395 one
396 two
397 ## END
398 ## OK osh stdout-json: ""
399 ## OK osh status: 2
400
401 #### alias used in subshell and command sub
402 # This spec seems to be contradictoary?
403 # http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_03_01
404 # "When used as specified by this volume of POSIX.1-2017, alias definitions
405 # shall not be inherited by separate invocations of the shell or by the utility
406 # execution environments invoked by the shell; see Shell Execution
407 # Environment."
408 shopt -s expand_aliases
409 alias echo_='echo [ '
410 ( echo_ subshell; )
411 echo $(echo_ commandsub)
412 ## STDOUT:
413 [ subshell
414 [ commandsub
415 ## END
416
417 #### alias used in here doc
418 shopt -s expand_aliases
419 alias echo_='echo [ '
420 cat <<EOF
421 $(echo_ ])
422 EOF
423 ## STDOUT:
424 [ ]
425 ## END
426
427 #### here doc inside alias
428 shopt -s expand_aliases
429 alias c='cat <<EOF
430 $(echo hi)
431 EOF
432 '
433 c
434 ## STDOUT:
435 hi
436 ## END
437 ## BUG bash stdout-json: ""
438 ## BUG bash status: 127
439
440 #### Corner case: alias inside LHS array arithmetic expression
441 shopt -s expand_aliases
442 alias zero='echo 0'
443 a[$(zero)]=ZERO
444 a[1]=ONE
445 argv.py "${a[@]}"
446 ## STDOUT:
447 ['ZERO', 'ONE']
448 ## END
449 ## N-I dash stdout-json: ""
450 ## N-I dash status: 2
451 ## N-I zsh stdout-json: ""
452 ## N-I zsh status: 1
453
454 #### Alias that is pipeline
455 shopt -s expand_aliases
456 alias t1='echo hi|wc -c'
457 t1
458 ## STDOUT:
459 3
460 ## END
461
462 #### Alias that is && || ;
463 shopt -s expand_aliases
464 alias t1='echo one && echo two && echo 3 | wc -l;
465 echo four'
466 t1
467 ## STDOUT:
468 one
469 two
470 1
471 four
472 ## END
473
474 #### Alias and command sub (bug regression)
475 cd $TMP
476 shopt -s expand_aliases
477 echo foo bar > tmp.txt
478 alias a=argv.py
479 a `cat tmp.txt`
480 ## stdout: ['foo', 'bar']
481
482 #### Alias and arithmetic
483 shopt -s expand_aliases
484 alias a=argv.py
485 a $((1 + 2))
486 ## stdout: ['3']
487
488 #### Alias and PS4
489 # dash enters an infinite loop!
490 case $SH in
491 dash)
492 exit 1
493 ;;
494 esac
495
496 set -x
497 PS4='+$(echo trace) '
498 shopt -s expand_aliases
499 alias a=argv.py
500 a foo bar
501 ## stdout: ['foo', 'bar']
502 ## BUG dash status: 1
503 ## BUG dash stdout-json: ""
504
505 #### alias with keywords
506 # from issue #299
507 shopt -s expand_aliases
508 alias a=
509
510 # both of these fail to parse in OSH
511 # this is because of our cleaner evaluation model
512
513 a (( var = 0 ))
514 #a case x in x) true;; esac
515
516 echo done
517 ## stdout: done
518 ## OK osh status: 2
519 ## OK osh stdout-json: ""