help-bash
[Top][All Lists]
Advanced

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

Re: How does quote removal work with alternative forms of parameter expa


From: Koichi Murase
Subject: Re: How does quote removal work with alternative forms of parameter expansion?
Date: Mon, 27 May 2024 11:03:59 +0900

2024年5月27日(月) 9:38 Philippe Cerfon <philcerf@gmail.com>:
> Just out of curiosity:
> Is there any place in the POSIX text from where I could have deduced
> that behavior?

Lawrence seems to have already explained it nicely in a reply:

[1] https://lists.gnu.org/archive/html/help-bash/2024-05/msg00092.html

> > > So it seems as if there'd be first a "normal" quote removal on the
> > > word part of these parameter expansion forms and then the outer quote
> > > removal, right?
> >
> > Yes.
>
> And we're still taking about only the # ## % and %% forms here, right?

Ah, yes, that part was quoted from the part you discussed ${var#...},
etc. Sorry for the confusing quoting.

> Why does:
> $ bar='1 2'
> $ printf '%s\n' "${unset_var:-"$bar"}"
>
> then result in:
> 1 2
> and not:
> 1
> 2
>
> ?
> If the above would be like ""$bar"" I'd have thought the $bar is not
> quoted and thus produces separate files?

It's still "something like" x=""$bar"", and not exactly equivalent to
directly writing «printf ""$bar""». Just the processing of quoting and
expansions are affected by the inside double quotes. I think you can
regard «"» as a special character to toggle the behavior in this
context.

> Is this coupling then also POSIX behaviour?

POSIX specifies it to be unspecified as Lawrence has explained [1].

> Is there even a way, when using the + - = ? forms, to use quoting in
> the word part and still be portable?
> It would seem to me that backslash quoting is then the only portable way?
>
> [...]
>
> I guess I do (kind of) understand now how it's done in bash, but which
> rules should one follow if one tries to program portable?

There are even more cases that one needs to be careful about the
quoting nested in the double-quoted parameter expansions: `shopt -s
patsub_replacement' and `shopt -s compat42' in addition to the
mentioned `shopt -s extquote'. I wrote some points about
`patsub_replacement' and `compat42' in [2] before, (though it doesn't
cover the present case).

[2] 
https://github.com/scop/bash-completion/blob/main/doc/styleguide.md#patsub_replacement-for-array-elements

My conclusion is that we shouldn't double-quote parameter expansions
when any inside quoting is involved. I always assign a result to a
variable with no double-quoting and use it in the command. I would
write your example as

  foo() {
    local error1_format_string=${5:-'some default string with backticks `%s`'}
    printf "$error1_format_string" "$foo"
  }

The right-hand side (RHS) of variable assignments is not subject to
word splitting and pathname expansions, so one usually doesn't need to
quote the parameter expansions.

When I want to include shell special characters such as
[~[:space:]<>&|;\"`'] in the right-hand side, I quote them separately
as

  local string='~/a b c '${5:-'something `%s`'}'<>&|;\"`'\'' x y z'

This is also nice with text editors highlighting the entire "..." as
the single literal and not highlighting the inside structures (though
we can say such a text editor could be improved).

When I want to specify it as an argument to another command (instead
of RHS of assignments), I'd assign it in a variable and then specify
"$var" to the command: For example, instead of «printf '%s\n'
"${x:-'$bar'}"», I'd write

  local v=${x:-'$bar'} # if you want the literal '$bar'
  printf '%s\n' "$v"

The remaining case is that the nested quotation in the array
assignments of the form arr2=("${arr1[@]...}"). The elements of array
assignments are still subject to word splitting and pathname
expansions, so one cannot simply omit the outside double quotes. In
this case, I don't know a good solution so would recommend people to
first perform the simple assignments, and then modify the elements one
by one:

  local arr2 key
  arr2=("${arr1[@]}")
  for key in "${!arr2[@]}"; do
    arr2[$key]=${arr2[$key]...}
  done

This is also mentioned in [2].

> > Another case you might be interested in is
> >
> > x="${1:-"foo \bar"foo\ bar"foo\ bar"}"
>
> What especially do you mean here?

Sorry, as I've already mentioned in [3] that was just a leftover of
editing my reply.

[3] https://lists.gnu.org/archive/html/help-bash/2024-05/msg00094.html

--
Koichi



reply via email to

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