Re: `declare -f "a="' fails unnecessarily

2022-12-04 Thread Dale R. Worley
Emanuele Torre  writes:
> `declare -f "something="' fails with the following error:
>
> $ declare -f 'a=x'
> bash: declare: cannot use `-f' to make functions

> That error is not very useful. Bash makes `declare -f' fail with that
> error when an argument looks like an assignment.

It's an interesting mess.  Looking at the definition of "declare", the
"=" is used to separate the name from the value being assigned to the
name:

   declare [-aAfFgiIlnrtux] [-p] [name[=value] ...]

So the statement above is an attempt to declare the name "a" as a
function, with the value somehow being "x".

There's a difficulty because recent Bashes have allowed function names
that are not "names" in the Bash sense:

   fname () compound-command [redirection]
   function fname [()] compound-command [redirection]
  This  defines  a function named fname.  [...]
  When  in  posix  mode,
  fname must be a valid shell name and may not be the name of one
  of the POSIX special builtins.  In  default  mode,  a  function
  name  can  be  any unquoted shell word that does not contain $.

   name   A  word  consisting  only of alphanumeric characters and under‐
  scores, and beginning with an alphabetic character or an under‐
  score.  Also referred to as an identifier.

In default mode, you actually can do
$ function a=b { printf hi\\n; }
though you can't execute it:
$ a=b foo
bash: foo: command not found

You say the error is not very useful, but it seems to me that the error
is doing exactly what is intended; you *shouldn't* have an argument that
looks like an assignment.  IMO the fact that you can use "function" to
declare a function with "=" in its name is a mis-feature.

Dale



Re: `declare -f "a="' fails unnecessarily

2022-12-04 Thread Andreas Schwab
On Dez 04 2022, Dale R. Worley wrote:

> In default mode, you actually can do
> $ function a=b { printf hi\\n; }
> though you can't execute it:
> $ a=b foo
> bash: foo: command not found

You just have to quote any part of the function name upto the equal sign
to stop if from being interpreted as an assignment.

$ \a=b foo
hi

-- 
Andreas Schwab, sch...@linux-m68k.org
GPG Key fingerprint = 7578 EB47 D4E5 4D69 2510  2552 DF73 E780 A9DA AEC1
"And now for something completely different."



Antw: [EXT] Re: declare XXX=$(false);echo $?

2022-12-04 Thread Ulrich Windl
Dale, thanks for explaining.

So basically the behavior is as documented (not a bug), but the design
decision was poor:

  declare a
  a=b

has a different semantic as
  declare a=b

which I consider to be bad.

Ulrich

>>> "Dale R. Worley"  schrieb am 02.12.2022 um 17:39 in
Nachricht <878rjpahfz@hobgoblin.ariadne.com>:
> Chet Ramey  writes:
>> On 12/2/22 5:28 AM, Ulrich Windl wrote:
>>> Surprisingly "declare XXX=$(false);echo $?" outputs "0" (not "1")
>>> There is no indication in the manual page that "declare" ignores the
>>exit code of commands being executed to set values.
>>
>> Why do you think it should? `declare' has a well-defined return status.
> 
> There it is, end of "SIMPLE COMMAND EXPANSION":
> 
>If  there is a command name left after expansion, execution proceeds

> as
>described below.  Otherwise, the command exits.  If one of  the  
> expan‐
>sions  contained a command substitution, the exit status of the 
> command
>is the exit status of the  last  command  substitution  performed.  

> If
>there were no command substitutions, the command exits with a status

> of
>zero.
> 
> and:
> 
>declare [-aAfFgiIlnrtux] [-p] [name[=value] ...]
>typeset [-aAfFgiIlnrtux] [-p] [name[=value] ...]
> [...]
>   The return value
>   is 0 unless an invalid option is encountered, an attempt is 
> made
>   to define a function using ``-f foo=bar'', an attempt is made

> to
>   assign a value to a readonly variable, an attempt is made to 
> as‐
>   sign a value to an array variable without using the compound 
> as‐
>   signment  syntax  (see  Arrays above), one of the names is not

> a
>   valid shell variable name, an attempt is made to turn off  
> read‐
>   only  status for a readonly variable, an attempt is made to 
> turn
>   off array status for an array variable, or an attempt is made

> to
>   display a non-existent function with -f.
> 
> If you input "XXX=$(false)", there isn't a command name, it's a sequence
> of assignments, and "the exit status of the command is the exit status
> of the last command substitution performed".  But if you input "declare
> XXX=$(false)", you're executing the "declare" command, and the exit
> status doesn't depend on the command substitution.
> 
> Dale






Antw: [EXT] Re: declare XXX=$(false);echo $?

2022-12-04 Thread Ulrich Windl
>>> Martin D Kealey  schrieb am 03.12.2022 um 02:26 in
Nachricht
:

...

> Found in the real code (intended to trigger a bug):
>> declare ERRORS=0 ARGS=$(getopt -o "$S_OPTS" -l "$L_OPTS" -n "$0" --
>> "$@")
>>
> if [ $? -ne 0 ]; then
>> usage
>> fi
>>
> 
> That is a well-known anti-pattern that should be avoided.
> 
> Some working equivalents would be:
> ```
> declare ARGS=$( getopt -o "$S_OPTS" -l "$L_OPTS" -n "$0" -- "$@" )
> ERRORS=$?
> (( ERRORS == 0 )) || usage
> ```
> or:
> ```
> declare ARGS ERRORS=0
> ARGS=$( getopt -o "$S_OPTS" -l "$L_OPTS" -n "$0" -- "$@" ) ||
> usage
> ```
> 
> -Martin
> 
> (PS: "Working" would be questionable, since putting `getopt` in a subshell
> prevents it from updating "$@"; and `test $? = 0` and its variants are
> pointless clutter.)

Well, the code proved to work, and the pattern was found in 
/usr/share/getopt/getopt-parse.bash (of BASH 4.3):

...
# Note that we use "$@" to let each command-line parameter expand to a
# separate word. The quotes around "$@" are essential!
# We need TEMP as the 'eval set --' would nuke the return value of getopt.
TEMP=$(getopt -o 'ab:c::' --long 'a-long,b-long:,c-long::' -n 'example.bash' -- 
"$@")

if [ $? -ne 0 ]; then
echo 'Terminating...' >&2
exit 1
fi
...