One of the pitfalls of portable shell programming is that 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 case
statement:
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 underquotes its arguments.
As a result, the unbalanced ‘)’ generated by the premature
expansion of 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 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.
AS_CASE
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
expansion of 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.