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