[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Document that here strings don't support brace expansion.

From: Robert Elz
Subject: Re: Document that here strings don't support brace expansion.
Date: Wed, 15 Mar 2023 14:05:56 +0700

While it is probably useful to have every place where expansions occur list
which ones apply, perhaps it would (also) be useful to include the general
principle which controls all of this.

That is, expansions are performed, late (just as commands are about to
be run, after all parsing has finished, etc) in one of two basic contexts.

In one, like in arg lists to commands (also the list after "in" in a for
statement, and probablty more places I have forgotten), any number of words
can be produced.  In those, all the expansions typically happen.

In the other, exactly one word is required - like the word in var=${word}
or the word in case ${word} in, or (in bash, and perhaps a couple of other
shells) the word after a here string operator <<<${word} (and one more I
will come to in a minute).   [The word after a here-doc redirect operator (<<)
is strange for hysterical (oops: historical) reasons.]

In those, the syntax allows for only one word to be produced, so performing
field splitting, globbing, or brace expansion, all make no sense at all,
and so the basic assumption, unless specified otherwise, is that none of
those will happen.  (This is also why var="$@" is just plain stupid to write,
and has unspecified consequences, though people keep doing it).

quote removal always happens (everywhere).

The one other place where just one word is required, and is all that
makes sense, is after a redirect operator - the file name (or fd if the
operator is >& or similar).   That is >${word} (etc).   For most shells
none of the multi-word generating "expansions" happen there either, as
there has to be exactly one filename to write into (in this example), not
several.   However, at least when interactive, some shells perform globbing
at this point, but require that only a single filename match the pattern
(making it an error if more than one do) - just because users are too lazy
to type the entire filename -- or use a func like:

                var=$1; shift
                set -- "$@"
                case "$#" in
                1) test -e "$1" && {
                        eval "$var='$1'"
                        return 0
                   printf >&2 'No match\n'
                   return 1;
                printf >&2 'Ambiguous: %d files matched\n', "$#"
                return 1

Used like 
        setfn f a*.txt
after which, if it worked, you can use "$f" instead of the
filename you are too lazy to type, which is shorter than a
pattern guaranteed to only match one file will often be.

Further it doesn't risk failing later, if something you do
causes the pattern to later match more than one file (say you
create abc-new.txt as a new version of abc.txt or save the
old one as abc.was.txt).   "$f" doesn't change until you change it.

Bash allows filename expansion after a redirect, at least in 
interactive shells (haven't tested it for non-interactive), it
also performs brace expansion there - but that's largely guaranteed
to fail, so I have no idea why - no field splitting though.
Those exceptions to the general rule need to be clearly pointed out,
but explaining every time something requires just one word, that
some expansions don't happen, when in a context where it would be
insane if they did, seems overdone.

<<< requires a single word, as so all other redirect operators.
Brace expansion, which has as its whole purpose, generating multiple
words (as does field splitting, though it doesn't always succeed)
(globbing merely can) should be obviously inappropriate there (just as
it really should be after any redirect operator, which is why I
was surprised to see

        $ cat < a{1,2,3}
        -bash: a{1,2,3}: ambiguous redirect

rather than something more like
        -bash: a{1,2,3}: No such file or directory

The only example of brace expansion actually "working" in a
redirect, is something like

        $ cat <a{1..1}
        -bash: a1: No such file or directory

but I am unable to think of any situation where doing that is
better than just "cat <a1".   Even if the 1's were variables being
expanded, we know that either they have the same value, in which
case we only need to use one of them <a${var}  or they're different,
in which case we get an "ambiguous redirect error".   Crazy.


reply via email to

[Prev in Thread] Current Thread [Next in Thread]