On Wed, Mar 15, 2023, 8:07 AM Robert Elz <k...@munnari.oz.au> wrote: > 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: > > setfn() > { > var=$1; shift > set -- "$@" > case "$#" in > 1) test -e "$1" && { > eval "$var='$1'" > declare -g "$var=$1"
> return 0 > } > printf >&2 'No match\n' > return 1; > ;; > esac > 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. > > kre > > >