help-bash
[Top][All Lists]
Advanced

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

Re: Quoting


From: Greg Wooledge
Subject: Re: Quoting
Date: Wed, 28 Jul 2021 09:48:21 -0400

On Wed, Jul 28, 2021 at 03:14:07PM +0200, eduardo-chibas@caramail.com wrote:
> > Sent: Thursday, July 29, 2021 at 1:06 AM
> > From: "Greg Wooledge" <greg@wooledge.org>
> > To: help-bash@gnu.org
> > Subject: Re: Quoting
> >
> > On Wed, Jul 28, 2021 at 01:29:45PM +0200, eduardo-chibas@caramail.com wrote:
> > > Is is adequate using the following
> > >
> > > : ${fdir:=${@:$#}}
> > >
> > > Or is the following better
> > >
> > > : ${fdir:="${@:$#}"}
> >
> > Are you simply trying to put the *last* argument into a variable?
> 
> Yes
> 
> > [[ $fdir ]] || fdir=${!#}
> 
> I have read long winded discussions on the security implications of not 
> quoting
> variables.  Discussions are quite complicated.

Yes, it's an important subject, and some of the rules are a bit obscure.
I omitted quotes in my answer because they weren't necessary.  Because:

1) The [[ keyword has special parsing rules.  Word splitting does not
   occur.  Quotes are only necessary on the right hand side of the =
   operator if you want to perform a string match rather than a glob
   match.

   This is unique to the [[ command, which is a bash extension.

2) On the right hand side of a simple assignment (x=y), you can omit the
   quotes.  Word splitting does not occur.

   This is standard in all Bourne family/POSIX shells.

The construction that you tried to use is a weird mix of ancient
convention and bash extensions.  I'm not particularly fond of it, and I
don't recommend it due to its unreadability.  The fact that you couldn't
figure out how to write it properly, to the point that you had to ask
about it, is another reason to avoid it.

If you really want to go down that road, though, you have to tear it
apart and analyze it piece by piece.

First, you have this thing, which is POSIX standard:

: ${x:=y}

Word splitting is performed here, so ideally you'd want to quote the
expansion to prevent that.  It's not actually a security risk; more of
a hygienic practice.

: "${x:=y}"

That's the preferred form for this piece.

Next, you've got this piece on the inside:

"${@:$#}"

This is a variant of "$@".  "$@" is POSIX standard, and it expands to a
list of zero or more words.  Building from that, you've introduced a
bash extension, "${@:number}" which says to begin the list at a chosen
point, so that we have a (potentially) smaller list -- but still a list
of zero or more words.

Normally, if you wanted to store this list, you'd use an array variable.

However, you've carefully selected the "number" here so that the list is
always precisely one word long, rather than an arbitrary number of words.

In essence, you are denying the very nature of "$@" as a list.  This is
one of my objections to this construction.  It's extremely unintuitive.

However, if you insist on using this construction, then the quotes are
required here.  You need each word (or "the word") to remain intact,
and not undergo word splitting.

Now, it gets even *more* complicated because you're trying to assign
this list-that-is-really-just-one-word to a variable within a parameter
expansion using the arcane := syntax.

You *do* want to keep the quotes.

What you end up with, then, is:

: "${fdir:="${@:$#}"}"

And may the gods help anyone trying to read this script and figure out
what this line does.

I prefer  [[ $fdir ]] || fdir=${!#}  because I believe it's much easier
to understand.  ${!#} is a bash extension for "the last argument", and
it's shorter and simpler to understand than "${@:$#}" is.

If you prefer to put in the optional quotes, you can do that.

[[ "$fdir" ]] || fdir="${!#}"

works just as well.  Some people feel safer with the optional quotes
in place, and they don't hurt anything.



reply via email to

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