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