run-help typeset says:
-p [ n ]
If the -p option is given, parameters and values are
printed in the form of a typeset command with an assign-
ment, regardless of other flags and options. Note that
the -H flag on parameters is respected; no value will be
shown for these parameters.
Note it says parameters and values above.
If it do:
% typeset -p ZPLGM
typeset -A ZPLGM
Note no key-values above, however they do exist:
% echo $ZPLGM[PLUGINS_DIR]
/home/ravi/.config/zsh/.zplugin/plugins
Why doesn't typeset -p work as I expect?
How do I get typeset to print a statement which, when executed, would recreate the array?
Because the variable ZPLGM is defined with -H option.
unset foo
typeset -AH foo=([bar]=123)
# ^----here
echo $foo[bar]
typeset -p foo
123
typeset -A foo
typeset has an option -H, as the manual explains:
-H Hide value: specifies that typeset will not display the
value of the parameter when listing parameters; the dis-
play for such parameters is always as if the `+' flag had
been given. Use of the parameter is in other respects
normal, and the option does not apply if the parameter is
specified by name, or by pattern with the -m option.
This is on by default for the parameters in the
zsh/parameter and zsh/mapfile modules. Note, however,
that unlike the -h flag this is also useful for non-spe-
cial parameters.
unset foo
typeset -A foo=([bar]=123)
echo $foo[bar]
typeset -p foo
123
typeset -A foo=( [bar]=123 )
Related
I will be getting one command line argument in the script I'm writing which will itself be a space delimited list of the actual command line arguments. I'd like to set the arguments of the current script with these arguments. How might I accomplish that?
I'd like to use set -- but I'm not sure how this would work.
E.g.
Given arguments to my script: -a -b -c
echo $1 # prints "-a -b -c"
You can do this with set -- "${(z)1}". This will split $1 into words, handling quoting the same way the shell itself does:
% cat script.zsh
#!/usr/bin/env zsh
set -- "${(z)1}"
for arg; do
echo "==$arg=="
done
% ./script.zsh "-a -b -c -d'has spaces'"
==-a==
==-b==
==-c==
==-d'has spaces'==
If you also want to remove a level of quotes, use "${(#Q)${(z)1}}" instead.
To declare associative array programmatically, I tried this:
foo=bar
typeset -A "${foo}"=(
[key1]="hello world"
[key2]=baz
)
Whatever I try, I'm getting:
zsh: unknown sort specifier
How to declare an associative array using a variable to set its name ?
This might not be the best way, but you can use indirect parameter expansion after declaring the name to be an associative array.
foo=bar
typeset -A "${(p)foo}"
typeset "${(p)foo}[hello]=world"
You can also use set -A
foo=bar
set -A $foo hello world
It seems zsh doesn't honor globs inside variable patterns, in ${var##$pat} parameter expansions:
$ zsh -c 'pat=/*; var=/etc/; echo "$var $pat"; echo "${var##$pat}"'
/etc/ /*
/etc/
# sh result: empty
However, if $pat does not contain *, zsh and sh behave similarly:
$ zsh -c 'pat=/; var=/etc/; echo "$var $pat"; echo "${var##$pat}"'
/etc/ /
etc/
# sh result: same
zsh --emulate sh gives, of course, sh-compatible results. But if I want to stay in zsh emulation, is there any setopt option that changes this behavior? I've looked (briefly) in the docs and I can't really find the reason for this difference.
In zsh, variable contents will only be treated as a pattern if you
ask for that, with a ${~spec} expansion or the (very broad and therefore slightly dangerous) GLOB_SUBST option:
pat=/*t
var=/etc/
print "${var##$pat}"
#=> /etc/
print "${var##$~pat}"
#=> c/
setopt glob_subst
print "${var##$pat}"
#=> c/
This is described in the zshexpn man page, in the section for string substitution expansion ${name/pattern/repl}.
I'm trying to use zsh's zparseopts builtin to process some shell script arguments. The argument list may contain -- to indicate that subsequent values are regular inputs and not options, even if they begin with a hyphen.
When zparseopts modifies the argument list, it always removes the double hyphen. This makes it difficult to determine if an argument starting with a hyphen is an error.
A demonstration; -a and b are items to be processed:
set -- -x -- -a b
print -- $#
# => -x -- -a b
zparseopts -D -a opts -- x
print -- $#
# => -a b
In this next invocation, -a is an invalid option. But the zparseopts result is the same, so there isn't a way to find the error:
set -- -x -a b
print -- $#
# => -x -a b
zparseopts -D -a opts -- x
print -- $#
# => -a b
This workaround makes a copy of any arguments after a --, and later compares the copy with the result from zparseopts:
items=(${${#[(re)--,-1]}[2,-1]})
zparseopts -D -a opts -- x
if [[ ${#items} -ne ${##} ]]; then
items=($#)
if [[ ${idx::=${items[(i)-*]}} -le ${#items} ]]; then
print "unknown option: [${items[idx]}] items: [$items]"
exit 1
fi
fi
The workaround does what I need, but it seems strange that zparseopts doesn't support this directly. Am I missing something?
I found this in prezto source code:
# Set the command name, or in the case of sudo or ssh, the next command.
local cmd="${${2[(wr)^(*=*|sudo|ssh|-*)]}:t}"
I've been reading zsh doc a lot but getting nowhere close to what this is about. In experimentation on the shell itself it seems to indicate the [] is some arithmetic thing, which makes sense, but I don't see the part that explains how (w) is supposed to work. It seems to be some magical operator that applies to the math expression...
slu#ubuntu-sluvm ~/.zprezto ❯❯❯ VAR="one two three four"
slu#ubuntu-sluvm ~/.zprezto ❯❯❯ echo ${VAR[2]}
n
slu#ubuntu-sluvm ~/.zprezto ❯❯❯ echo ${VAR[(w)2]}
two
slu#ubuntu-sluvm ~/.zprezto ❯❯❯ echo ${VAR[(w)]}
zsh: bad math expression: empty string
slu#ubuntu-sluvm ~/.zprezto ❯❯❯
It looks pretty messy at first glance, but once you break it into its parts it's quite simple. This is an example of parameter expansion and extended globbing support in ZSH. If you look higher up in the function from which this code sample is, you'll see they set:
emulate -L zsh
setopt EXTENDED_GLOB
So now let's break apart the line you have there:
${
${
2[ # Expand the 2nd argument
(wr) # Match a word
^(*=*|=|sudo|ssh|-*) # Do not match *=*, =, sudo, ssh, or -*
]
}
:t} # If it is a path, return only the filename
You can test this by creating a sample script like this:
#!/bin/zsh
emulate -L zsh
setopt EXTENDED_GLOB
echo "${$1[(wr)^(*=*|sudo|ssh|-*)]}:t}" # changed 2 to 1, otherwise identical
Here's what it outputs:
$ ./test.sh '/bin/zsh'
zsh
$ ./test.sh 'sudo test'
test
$ ./test.sh 'sudo --flag test'
test
$ ./test.sh 'ssh -o=value test'
test
$ ./test.sh 'test'
test
For more information, see the documentation on expansion and csh-style modifiers.