Zsh Mailing List Archive
Messages sorted by: Reverse Date, Date, Thread, Author

Re: What is the '+' option of the 'typeset' builtin supposed to do?



Thanks for your explanations. I see now that my main error was to think that '+' was a mechanism to print parameters, while in reality it's just a mechanism to suppress the printing of parameter values.

I spent some time looking into the implementation and (much more time) testing the behavior of 'typeset'. This led to the patch below that better documents the base behavior of 'typeset' and its '-m', '-p', and '+' options (for convenience, I have included the rendered updated text). Unfortunately, things are often a little complicated. There are a number of inconsistencies that don't seem to be very useful. If we could eliminate some of them, the documentation could be trimmed.

I can't guarantee that everything is 100% accurate. As this is mainly based on tests (rather than on code inspection), it could be that some things are even more complicated than described.
 
> > 'typeset -i + str int' prints nothing but turns the listed parameters into integers => KO
> > Why does that change the type of the parameters?

One other thing to note here is that using '+' standalone when it is
not the only and final argument is an "all bets are off" situation.

Surprisingly, '+' is only checked for here. It's only taken into account if there are no arguments and no '-p' and its only effect is to disable the printing of parameter values.

> 'typeset -i -t +' prints the name of all integer parameters => okish
> Why not print only tagged integer parameters?

This probably comes down to an oversight in how printparamnode() is
implemented.  Tagging has always been a bit of a non-feature feature.

Actually, whenever parameters are filtered by attributes, the rule seems to be that all parameters that have at least one of the specified attributes are included. Is that by design or by accident? I have no clue but it's certainly because of this. Ksh, at least with 'typeset -p', seems to require all attributes to be set. Bash seems to require the type attribute if one is specified and that at least one of the specified non-type attributes is set (so a mix of Zsh's and ksh's behaviors).

> 'typeset -i + str int' prints nothing but turns the listed parameters into integers => KO
> Why does that change the type of the parameters?

See above.  I've no strong feelings about whether this should be
changed or merely properly documented.  Anyone?

I was confused by this because I thought that '+' was a mechanism to print parameters but now that I understand that it's just a mechanism to suppress the printing of parameter values, it makes sense that this behaves like 'typeset -i + str int'. It just needs a better documentation of '+'.

> 'typeset + undef' prints nothing and creates a string parameter => KO
> Why does this create a parameter?

This probably an accidental effect of the "assignments always win" behavior.

For the same reason, this is equivalent to  'typeset undef'.

Here are my comments and questions about the current implementation, or rather about the behavior documented below (the actual implementation might hide a few more surprises):
  • It's unfortunate that when 'typeset' only prints existing parameters, attribute flags aren't systematically used for filtering. 'typeset -p' simply ignores them even though filtering could be useful. For example, one could compute parameter names and then only print ones that are integers: 'local names=$(compute-parameter-names); typeset -p -i $names'.
  • Even more embarrassing is that with 'typeset -p +m', attribute flags introduced with '+' behave differently from all other cases where attribute flags are used for filtering.
  • The option '+' suppresses the printing of parameter values but only when there are no arguments and no '-p' option. This could be generalized. Not printing values could be useful even for 'typeset -p'. The option could be changed to work in all situations.
  • Another mechanism to suppress the printing of parameter values is to introduce attribute flags with '+' rather than '-'. However, it doesn't work with 'typeset -p' and with 'typeset' it's enough that one attribute flag uses '+', while with 'typeset +m' all specified attribute flags must use '+'. This too could be generalized to always work and always either require just one '+' or a '+' for all attribute flags.
  • If all of the above could be done, then filtering by attribute flags could be described just once as a general mechanism reused by different variants of 'typeset'.
  • When multiple attribute flags are specified for filtering, all parameters that have at least one of them rather than all of them are considered. The latter, à la ksh, looks more useful as the former can also be obtained by running a separate 'typeset' command for each attribute flag.
  • One thing that I find particularly disturbing is that with 'typeset +m', 'name=value' arguments still behave as with 'typeset -m' and modify matching parameters while 'name' arguments behave completely differently and instead of modifying or creating matching parameters only print them. It would be cleaner if 'typeset +m', like 'typeset -p', ignored any 'value' and only ever printed matching parameters.
  • The difference between 'typeset -p -m' and 'typeset -p +m' looks strange and not very useful to me. Do we really need two different versions?
  • It's also strange that with 'typeset +m', the absence of attribute flags enables the printing of attributes and disables the printing of values, while with 'typeset' it only does the former.
  • Currently 'typeset -n +m "ref*"=var' (but not 'typeset -n -m "ref*"=var') is allowed and changes all 'ref*' parameters into named references. This is a bug.
I don't know how much leeway we have in changing existing behavior. In many cases it would only affect rather complex combinations of flags that hopefully only show up in very few existing scripts. The benefit would be more consistent and easier to document behaviors.

  (depends on workers/55150)

Updated typeset base behavior documentation:

Except as noted below for control flags that change the behavior, a parameter is created for each name that does not already refer to one. When inside a function, the new parameter is created in a scope associated with the function and unset again when the function completes. A new parameter is created even if one already exists in the global scope or in the scope of an enclosing function. See Local Parameters. If name refers to a special shell parameter, the same rules apply but the new local parameter retains the special attributes of the shell parameter.

For each ‘name=value’ assignment, the parameter name is set to value. If the assignment is omitted and name does not refer to an existing parameter, a new parameter is initialized to empty string, zero, or empty array (as appropriate), unless the shell option TYPESET_TO_UNSET is set. When that option is set, the parameter remains unset.

If any attribute flags listed below are given along with name, each specified attribute is applied to the parameter name. Attributes introduced using ‘-’ are turned on. Attributes introduced using ‘+’ are turned off.

If the shell option TYPESET_SILENT is not set and no attribute flags are present, then for each name with no specified value that refers to a parameter that already exists, the name and value of the parameter are printed in the form of an assignment.

If no name is present, the names and values of all parameters are printed. In this case the attribute flags restrict the display to only those parameters that have at least one of the specified attributes turned on. If any attribute flag is introduced using ‘+’ rather than ‘-’, no parameter values are printed. If no attribute flags are given, then each parameter name printed is preceded by a list of the attributes of that parameter (array, association, exported, float, integer, readonly, or undefined for autoloaded parameters not yet loaded). Ordinary scalar string parameters have no attributes, for those no attributes are printed. Printing of parameter attributes can also be forced with the control flag +m described below.

[… same as before …]

Updated + documentation:

If ‘+’ appears by itself in a separate word as the last option, the printing of parameter values (or function bodies with the -f flag) is suppressed. However, for parameters, only if no name arguments are present; if name arguments are present, ‘+’ is ignored. It is an error for any other options to follow ‘+’ and ‘+’ must precede any argument.

Updated -m documentation:

If the -m flag is given the name arguments are taken as patterns (use quoting to prevent these from being interpreted as file patterns). For each ‘name=value’ argument, value is assigned to all parameters with names matching the pattern name. If any attribute flags are present, the specified attributes are also applied to the parameters. For each name argument, if any attribute flags are present, the specified attributes are applied to all parameters (functions with the -f flag) with matching names. If no attribute flags are present, the names and values of the parameters (or names and bodies of the functions with the -f flag) are printed (the shell option TYPESET_SILENT is not used in this case).

If the +g flag is combined with -m, a new local parameter is created for every matching parameter that is not already local. Otherwise values are assigned and attributes are applied to the existing parameters. Note that the -n type attribute is not allowed with -m; named references cannot be created this way.

The +m flag behaves the same as the -m flag for ‘name=value’ arguments. For name arguments, the names and values of all parameters with matching names are printed; unlike with -m, no parameters are created or modified. If attribute flags are present, only parameters with matching names that have at least one of the specified attributes turned on are printed. If all attribute flags are introduced using ‘+’, parameter values are not printed. With no attribute flags, each parameter name printed is preceded by the parameter attributes and no parameter values are printed.

Note that -m and +m are ignored if no patterns are given, so ‘typeset -m’ displays attributes.

Updated -p documentation:

If the -p option is given, each parameter is printed in the form of a typeset command with an assignment. If no attribute flags and no name arguments are present, all parameters are printed. If attribute flags are present but no name arguments, only parameters that have at least one of the specified attributes turned on are printed. If name arguments are present, only the named parameters are printed; attribute flags and value assignments are ignored.

If -m or +m is also present, the name arguments are taken as patterns (use quoting to prevent these from being interpreted as file patterns) and all parameters with matching names are printed. If attribute flags are present, only parameters with matching names that have at least one of the specified attributes turned on are printed. With +m, attributes introduced using both, ‘-’ and ‘+’, are considered. With +m, attributes introduced using ‘+’ are ignored, except if there are only such attributes and a type attribute is given. In that case, only parameters with matching names that do not have that type are printed. If no patterns are given, the -m and +m flags are ignored.

-p may be followed by an optional integer argument. Currently only the value 1 is supported. In this case arrays and associative arrays are printed with newlines between indented elements for readability.

The names and values of readonly special parameters (most of the parameters marked ‘<S>’ in Parameters Set by the Shell, except those documented as settable) are not printed with -p because to execute those typeset commands would cause errors. However, these parameters are printed when they have been made local to the scope where ‘typeset -p’ is run. Note also that the -H flag on parameters is respected; no value will be shown for these parameters.

Philippe

diff --git a/Doc/Zsh/builtins.yo b/Doc/Zsh/builtins.yo
index 51c994e98..17820c8c8 100644
--- a/Doc/Zsh/builtins.yo
+++ b/Doc/Zsh/builtins.yo
@@ -1930,35 +1930,47 @@ Set or display attributes and values for shell parameters.
 
 The type attribute flag tt(-n) and the control flags tt(-T) and tt(-f)
 change the set of valid additional flags as well as the meaning of
-some of them, see below the description of tt(-n), tt(-T) and tt(-f).
+some of them, see their description below.
 
 Except as noted below for control flags that change the behavior,
 a parameter is created for each var(name) that does not already refer
-to one.  When inside a function, a new parameter is created for every
-var(name) (even those that already exist), and is unset again when the
-function completes.  See sectref(Local Parameters)(zshparam).
-The same rules apply to special shell parameters, which
-retain their special attributes when made local.
-
-For each var(name)tt(=)var(value) assignment, the parameter
+to one.  When inside a function, the new parameter is created in a
+scope associated with the function and unset again when the function
+completes.  A new parameter is created even if one already exists in
+the global scope or in the scope of an enclosing function.  See
+sectref(Local Parameters)(zshparam).  If var(name) refers to a special
+shell parameter, the same rules apply but the new local parameter
+retains the special attributes of the shell parameter.
+
+For each `var(name)tt(=)var(value)' assignment, the parameter
 var(name) is set to var(value).  If the assignment is omitted and var(name)
 does em(not) refer to an existing parameter, a new parameter is initialized
 to empty string, zero, or empty array (as appropriate), em(unless) the
 shell option tt(TYPESET_TO_UNSET) is set.  When that option is set,
-the parameter attributes are recorded but the parameter remains unset.
+the parameter remains unset.
+
+If any attribute flags listed below are given along with var(name),
+each specified attribute is applied to the parameter var(name).
+Attributes introduced using `tt(-)' are turned on. Attributes
+introduced using `tt(PLUS())' are turned off.
 
-If the shell option tt(TYPESET_SILENT) is not set, for each remaining
-var(name) that refers to a parameter that is already set, the name and
-value of the parameter are printed in the form of an assignment.
-Nothing is printed for newly-created parameters, or when any attribute
-flags listed below are given along with the var(name).  Using
-`tt(PLUS())' instead of minus to introduce an attribute turns it off.
+If the shell option tt(TYPESET_SILENT) is not set and no attribute
+flags are present, then for each var(name) with no specified
+var(value) that refers to a parameter that already exists, the name
+and value of the parameter are printed in the form of an assignment.
 
 If no var(name) is present, the names and values of all parameters are
 printed.  In this case the attribute flags restrict the display to only
-those parameters that have the specified attributes, and using `tt(PLUS())'
-rather than `tt(-)' to introduce the flag suppresses printing of the values
-of parameters when there is no parameter name.
+those parameters that have at least one of the specified attributes
+turned on.  If any attribute flag is introduced using `tt(PLUS())'
+rather than `tt(-)', no parameter values are printed.  If no attribute
+flags are given, then each parameter name printed is preceded by a
+list of the attributes of that parameter (tt(array), tt(association),
+tt(exported), tt(float), tt(integer), tt(readonly), or tt(undefined)
+for autoloaded parameters not yet loaded).  Ordinary scalar string
+parameters have no attributes, for those no attributes are printed.
+Printing of parameter attributes can also be forced with the control
+flag tt(+m) described below.
 
 All forms of the command handle scalar assignment.  Array assignment is
 possible if any of the reserved words tt(declare), tt(export), tt(float),
@@ -2038,31 +2050,16 @@ To initialize a parameter var(param) to a command output and mark it readonly,
 use `tt(typeset -r )var(param)' or `tt(readonly )var(param)' after the parameter
 assignment statement.
 
-If no attribute flags are given, and either no var(name) arguments are
-present or the flag tt(+m) is used, then each parameter name printed is
-preceded by a list of the attributes of that parameter (tt(array),
-tt(association), tt(exported), tt(float), tt(integer), tt(readonly),
-or tt(undefined) for autoloaded parameters not yet loaded).  If tt(+m) is
-used with attribute flags, and all those flags are introduced with
-`tt(PLUS())', the matching parameter names are printed but their values
-are not.
-
 The following control flags change the behavior of tt(typeset):
 
 startitem()
 item(tt(PLUS()))(
-If `tt(PLUS())' appears by itself in a separate word as the last option,
-then the names of all parameters (functions with tt(-f)) are printed, but
-the values (function bodies) are not.  If var(name) arguments appear,
-both those names and their values are printed in the form of assignments.
-It is an error for any other options to follow `tt(PLUS())', but the
-effect of `tt(PLUS())' is as if all attribute flags which precede it were
-given with a `tt(PLUS())' prefix.  For example, `tt(typeset -U PLUS())' is
-equivalent to `tt(typeset +U)' and displays the names of all arrays having
-the uniqueness attribute, whereas `tt(typeset -f -U PLUS())' displays the
-names of all autoloadable functions.  If `tt(PLUS())' is the only option,
-then type information (array, readonly, etc.) is also printed for each
-parameter, in the same manner as `tt(typeset +m "*")'.
+If `tt(PLUS())' appears by itself in a separate word as the last
+option, the printing of parameter values (or function bodies with the
+tt(-f) flag) is suppressed.  However, for parameters, only if no
+var(name) arguments are present; if var(name) arguments are present,
+`tt(PLUS())' is ignored.  It is an error for any other options to
+follow `tt(PLUS())' and `tt(PLUS())' must precede any argument.
 )
 item(tt(-f))(
 The names refer to functions rather than parameters.  No assignments
@@ -2093,27 +2090,57 @@ combination with tt(-m) (see below).
 item(tt(-m))(
 If the tt(-m) flag is given the var(name) arguments are taken as patterns
 (use quoting to prevent these from being interpreted as file patterns).
-With no attribute flags, all parameters (or functions with the tt(-f)
-flag) with matching names are printed (the shell option tt(TYPESET_SILENT)
-is not used in this case).
+For each `var(name)tt(=)var(value)' argument, var(value) is assigned
+to all parameters with names matching the pattern var(name). If any
+attribute flags are present, the specified attributes are also applied
+to the parameters. For each var(name) argument, if any attribute flags
+are present, the specified attributes are applied to all parameters
+(functions with the tt(-f) flag) with matching names. If no attribute
+flags are present, the names and values of the parameters (or names
+and bodies of the functions with the tt(-f) flag) are printed (the
+shell option tt(TYPESET_SILENT) is not used in this case).
 
 If the tt(+g) flag is combined with tt(-m), a new local parameter is
 created for every matching parameter that is not already local.  Otherwise
-tt(-m) applies all other flags or assignments to the existing parameters,
-except that the tt(-n) option cannot create named references in this way.
-
-Except when assignments are made with `var(name)tt(=)var(value)', using
-tt(+m) forces the matching parameters and their attributes to be printed,
-even inside a function.  Note that tt(-m) is ignored if no patterns are
-given, so `tt(typeset -m)' displays attributes but `tt(typeset -a +m)'
-does not.  Ordinary scalar string parameters have no attributes, so for
-those tt(+m) prints only the names.
+values are assigned and attributes are applied to the existing
+parameters.  Note that the tt(-n) type attribute is not allowed with
+tt(-m); named references cannot be created this way.
+
+The tt(+m) flag behaves the same as the tt(-m) flag for
+`var(name)tt(=)var(value)' arguments.  For var(name) arguments, the
+names and values of all parameters with matching names are printed;
+unlike with tt(-m), no parameters are created or modified. If
+attribute flags are present, only parameters with matching names that
+have at least one of the specified attributes turned on are
+printed. If all attribute flags are introduced using `tt(PLUS())',
+parameter values are em(not) printed. With no attribute flags, each
+parameter name printed is preceded by the parameter attributes and no
+parameter values are printed.
+
+Note that tt(-m) and tt(+m) are ignored if no patterns are given, so
+`tt(typeset -m)' displays attributes.
 )
 item(tt(-p) [ var(n) ])(
-If the tt(-p) option is given, parameters and values are printed in the
-form of a typeset command with an assignment, regardless of other flags
-and options.  Note that the tt(-H) flag on parameters is respected; no
-value will be shown for these parameters.
+If the tt(-p) option is given, each parameter is printed in the form
+of a typeset command with an assignment.  If neither attribute flags
+nor var(name) arguments are present, all parameters are printed.  If
+only attribute flags are present, only parameters that have at least
+one of the specified attributes turned on are printed.  If var(name)
+arguments are present, only the named parameters are printed; any
+attribute flags and var(value) assignments are ignored.
+
+If tt(-m) or tt(+m) is also present, the var(name) arguments are taken
+as patterns (use quoting to prevent these from being interpreted as
+file patterns) and all parameters with matching names are printed.  If
+attribute flags are present, only parameters with matching names that
+have at least one of the specified attributes turned on are printed.
+With tt(+m), attributes introduced using both, `tt(-)' and
+`tt(PLUS())', are considered.  With tt(+m), attributes introduced
+using `tt(PLUS())' are ignored, except if there are only such
+attributes and a type attribute is given.  In that case, only
+parameters with matching names that do em(not) have that type are
+printed.  If no patterns are given, the tt(-m) and tt(+m) flags are
+ignored.
 
 tt(-p) may be followed by an optional integer argument.  Currently
 only the value tt(1) is supported.  In this case arrays and associative
@@ -2126,7 +2153,9 @@ sectref(Parameters Set by the Shell)(zshparam),
 except those documented as settable)
 are not printed with tt(-p) because to execute those typeset commands
 would cause errors.  However, these parameters are printed when they
-have been made local to the scope where `tt(typeset -p)' is run.
+have been made local to the scope where `tt(typeset -p)' is run.  Note
+also that the tt(-H) flag on parameters is respected; no value will be
+shown for these parameters.
 )
 item(tt(-T) [ var(scalar)[tt(=)var(value)] var(array)[tt(=LPAR())var(value) ...tt(RPAR())] [ var(sep) ] ])(
 This flag has a different meaning when used with tt(-f); see above.


Messages sorted by: Reverse Date, Date, Thread, Author