One of the pitfalls of portable shell programming is that
if you intend your script to run with obsolescent shells,
case statements require unbalanced parentheses.
See Limitations of Shell Builtins.
With syntax highlighting
editors, the presence of unbalanced ‘)’ can interfere with editors
that perform syntax highlighting of macro contents based on finding the
matching ‘(’. Another concern is how much editing must be done
when transferring code snippets between shell scripts and macro
definitions. But most importantly, the presence of unbalanced
parentheses can introduce expansion bugs.
For an example, here is an underquoted attempt to use the macro
my_case, which happens to expand to a portable
AC_DEFUN([my_case], [case $file_name in *.c) echo "C source code";; esac]) AS_IF(:, my_case)
In the above example, the
AS_IF call under-quotes its arguments.
As a result, the unbalanced ‘)’ generated by the premature
my_case results in expanding
AS_IF with a
truncated parameter, and the expansion is syntactically invalid:
if :; then case $file_name in *.c fi echo "C source code";; esac)
If nothing else, this should emphasize the importance of the quoting
arguments to macro calls. On the other hand, there are several
variations for defining
my_case to be more robust, even when used
without proper quoting, each with some benefits and some drawbacks.
AC_DEFUN([my_case], [case $file_name in (*.c) echo "C source code";; esac])
This is simple and provides balanced parentheses. Although this is not
portable to obsolescent shells (notably Solaris 10
platforms with these shells invariably have a more-modern shell
available somewhere so this approach typically suffices nowadays.
AC_DEFUN([my_case], [case $file_name in #( *.c) echo "C source code";; esac])
This version provides balanced parentheses to several editors, and can be copied and pasted into a terminal as is. Unfortunately, it is still unbalanced as an Autoconf argument, since ‘#(’ is an M4 comment that masks the normal properties of ‘(’.
AC_DEFUN([my_case], [case $file_name in @%:@( *.c) echo "C source code";; esac])
This version provides balanced parentheses to even more editors, and can be used as a balanced Autoconf argument. Unfortunately, it requires some editing before it can be copied and pasted into a terminal, and the use of the quadrigraph ‘@%:@’ for ‘#’ reduces readability.
AC_DEFUN([my_case], [case $file_name in *.c[)] echo "C source code";; esac])
This version quotes the ‘)’, so that it can be used as a balanced Autoconf argument. As written, this is not balanced to an editor, but it can be coupled with ‘[#(]’ to meet that need, too. However, it still requires some edits before it can be copied and pasted into a terminal.
AC_DEFUN([my_case], [[case $file_name in #( *.c) echo "C source code";; esac]])
Since the entire macro is double-quoted, there is no problem with using this as an Autoconf argument; and since the double-quoting is over the entire statement, this code can be easily copied and pasted into a terminal. However, the double quoting prevents the expansion of any macros inside the case statement, which may cause its own set of problems.
AC_DEFUN([my_case], [AS_CASE([$file_name], [*.c], [echo "C source code"])])
This version avoids the balancing issue altogether, by relying on
AS_CASE (see Common Shell Constructs); it also allows for the
AC_REQUIRE to occur prior to the entire case
statement, rather than within a branch of the case statement that might
not be taken. However, the abstraction comes with a penalty that it is
no longer a quick copy, paste, and edit to get back to shell code.