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
199 #### Invalid syntax of alias
200 shopt -s expand_aliases # bash requires this
201 alias echo_alias_= 'echo --; echo' # bad space here
202 echo_alias_ x
203 ## status: 127
204
205 #### Dynamic alias definition
206 shopt -s expand_aliases # bash requires this
207 x=x
208 name='echo_alias_'
209 val='=echo'
210 alias "$name$val"
211 echo_alias_ X
212 ## stdout: X
213
214 #### Alias name with punctuation
215 # NOTE: / is not OK in bash, but OK in other shells. Must less restrictive
216 # than var names.
217 shopt -s expand_aliases # bash requires this
218 alias e_+.~x='echo'
219 e_+.~x X
220 ## stdout: X
221
222 #### Syntax error after expansion
223 shopt -s expand_aliases # bash requires this
224 alias e_=';; oops'
225 e_ x
226 ## status: 2
227 ## OK mksh/zsh status: 1
228
229 #### Loop split across alias and arg works
230 shopt -s expand_aliases # bash requires this
231 alias e_='for i in 1 2 3; do echo $i;'
232 e_ done
233 ## STDOUT:
234 1
235 2
236 3
237 ## END
238
239 #### Loop split across alias in another way
240 shopt -s expand_aliases
241 alias e_='for i in 1 2 3; do echo '
242 e_ $i; done
243 ## STDOUT:
244 1
245 2
246 3
247 ## END
248
249 #### Loop split across both iterative and recursive aliases
250 shopt -s expand_aliases # bash requires this
251 alias FOR1='for '
252 alias FOR2='FOR1 '
253 alias eye1='i '
254 alias eye2='eye1 '
255 alias IN='in '
256 alias onetwo='$one "2" ' # NOTE: this does NOT work in any shell except bash.
257 one=1
258 FOR2 eye2 IN onetwo 3; do echo $i; done
259 ## STDOUT:
260 1
261 2
262 3
263 ## END
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 alias e_=echo
346 >$TMP/alias1.txt e_ 1
347 e_ >$TMP/alias2.txt 2
348 e_ 3 >$TMP/alias3.txt
349 cat $TMP/alias1.txt $TMP/alias2.txt $TMP/alias3.txt
350 ## BUG bash stdout-json: ""
351 ## STDOUT:
352 1
353 2
354 3
355 ## END
356
357 #### alias with environment bindings works
358 alias p_=printenv.py
359 FOO=1 printenv.py FOO
360 FOO=2 p_ FOO
361 ## STDOUT:
362 1
363 2
364 ## END
365 ## BUG bash status: 127
366 ## BUG bash STDOUT:
367 1
368 ## END
369
370 #### alias with line continuation in the middle
371 shopt -s expand_aliases
372 alias e_='echo '
373 alias one='ONE '
374 alias two='TWO '
375 alias three='THREE' # no trailing space
376 e_ one \
377 two one \
378 two three two \
379 one
380 ## stdout: ONE TWO ONE TWO THREE two one
381
382 #### alias for left brace
383 shopt -s expand_aliases
384 alias LEFT='{'
385 LEFT echo one; echo two; }
386 ## STDOUT:
387 one
388 two
389 ## END
390
391 #### alias for left paren
392 shopt -s expand_aliases
393 alias LEFT='('
394 LEFT echo one; echo two )
395 ## STDOUT:
396 one
397 two
398 ## END
399
400 #### alias used in subshell and command sub
401 # This spec seems to be contradictoary?
402 # http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_03_01
403 # "When used as specified by this volume of POSIX.1-2017, alias definitions
404 # shall not be inherited by separate invocations of the shell or by the utility
405 # execution environments invoked by the shell; see Shell Execution
406 # Environment."
407 shopt -s expand_aliases
408 alias echo_='echo [ '
409 ( echo_ subshell; )
410 echo $(echo_ commandsub)
411 ## STDOUT:
412 [ subshell
413 [ commandsub
414 ## END
415
416 #### alias used in here doc
417 shopt -s expand_aliases
418 alias echo_='echo [ '
419 cat <<EOF
420 $(echo_ ])
421 EOF
422 ## STDOUT:
423 [ ]
424 ## END
425
426 #### here doc inside alias
427 shopt -s expand_aliases
428 alias c='cat <<EOF
429 $(echo hi)
430 EOF
431 '
432 c
433 ## STDOUT:
434 hi
435 ## END
436 ## BUG bash stdout-json: ""
437 ## BUG bash status: 127
438
439 #### Corner case: alias inside LHS array arithmetic expression
440 shopt -s expand_aliases
441 alias zero='echo 0'
442 a[$(zero)]=ZERO
443 a[1]=ONE
444 argv.py "${a[@]}"
445 ## STDOUT:
446 ['ZERO', 'ONE']
447 ## END
448 ## N-I dash stdout-json: ""
449 ## N-I dash status: 2
450 ## N-I zsh stdout-json: ""
451 ## N-I zsh status: 1
452
453 #### Alias that is pipeline
454 shopt -s expand_aliases
455 alias t1='echo hi|wc -c'
456 t1
457 ## STDOUT:
458 3
459 ## END
460
461 #### Alias that is && || ;
462 shopt -s expand_aliases
463 alias t1='echo one && echo two && echo 3 | wc -l;
464 echo four'
465 t1
466 ## STDOUT:
467 one
468 two
469 1
470 four
471 ## END
472
473 #### Alias and command sub (bug regression)
474 shopt -s expand_aliases
475 echo foo bar > tmp.txt
476 alias a=argv.py
477 a `cat tmp.txt`
478 ## stdout: ['foo', 'bar']
479
480 #### Alias and arithmetic
481 shopt -s expand_aliases
482 alias a=argv.py
483 a $((1 + 2))
484 ## stdout: ['3']
485
486 #### Alias and PS4
487 # dash enters an infinite loop!
488 case $SH in
489 */dash)
490 exit 1
491 ;;
492 esac
493
494 set -x
495 PS4='+$(echo trace) '
496 shopt -s expand_aliases
497 alias a=argv.py
498 a foo bar
499 ## stdout: ['foo', 'bar']
500 ## BUG dash status: 1
501 ## BUG dash stdout-json: ""