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 doesn't work on a single line!
324 alias sayhi='echo hello'; sayhi same line
325 sayhi other line
326 ## STDOUT:
327 hello other line
328 ## END
329 ## BUG bash stdout-json: ""
330 ## BUG bash status: 127
331
332 #### Alias is respected inside eval
333 shopt -s expand_aliases
334 eval "alias sayhi='echo hello'
335 sayhi inside"
336 sayhi outside
337 ## STDOUT:
338 hello inside
339 hello outside
340 ## END
341 ## BUG zsh STDOUT:
342 hello outside
343 ## END
344
345 #### alias with redirects works
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 ## BUG bash stdout-json: ""
352 ## STDOUT:
353 1
354 2
355 3
356 ## END
357
358 #### alias with environment bindings works
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 ## BUG bash status: 127
367 ## BUG bash STDOUT:
368 1
369 ## END
370
371 #### alias with line continuation in the middle
372 shopt -s expand_aliases
373 alias e_='echo '
374 alias one='ONE '
375 alias two='TWO '
376 alias three='THREE' # no trailing space
377 e_ one \
378 two one \
379 two three two \
380 one
381 ## stdout: ONE TWO ONE TWO THREE two one
382
383 #### alias for left brace
384 shopt -s expand_aliases
385 alias LEFT='{'
386 LEFT echo one; echo two; }
387 ## STDOUT:
388 one
389 two
390 ## END
391
392 #### alias for left paren
393 shopt -s expand_aliases
394 alias LEFT='('
395 LEFT echo one; echo two )
396 ## STDOUT:
397 one
398 two
399 ## END
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 #### Corner case: alias inside LHS array arithmetic expression
428 shopt -s expand_aliases
429 alias zero='echo 0'
430 a[$(zero)]=ZERO
431 a[1]=ONE
432 argv.py "${a[@]}"
433 ## STDOUT:
434 ['ZERO', 'ONE']
435 ## END
436 ## N-I dash stdout-json: ""
437 ## N-I dash status: 2
438 ## N-I zsh stdout-json: ""
439 ## N-I zsh status: 1