How to add to an associative array in zsh? - zsh

I'm trying to change an element of a zsh associative array, but I can't find any clues on the correct syntax.
The PHP equivalent would be
$assoc['key'] = 'newvalue';
but how can I do this in zsh?
The documentation seems to be very confusing on this, as it assumes that once you've set up an associative array, you never want to change it.
EDIT: this is what I'm trying to do
% noglob ZSH_HIGHLIGHT_STYLES[globbing]='fg=yellow'
zsh: command not found: ZSH_HIGHLIGHT_STYLES[globbing]=fg=yellow
$ZSH_HIGHLIGHT_STYLES is defined by the zsh-syntax-highlighting plugin.

It's actually pretty simple, assuming you have an associative array.
typeset -A assoc
assoc[key]=newvalue

Related

zsh - how does the indexing for this associative array work?

The code below seems to be working correctly:
#!/bin/zsh
zparseopts -D -E -A opts f -foo
if [[ -n ${opts[(ie)-f]} || -n ${opts[(ie)--foo]} ]]; then
echo "foo is set."
else
echo "foo is not set."
fi
~/tmp > ./args.sh
foo is not set.
~/tmp > ./args.sh -f
foo is set.
~/tmp > ./args.sh --foo
foo is set.
What does the syntax for the index of opts mean i.e. (ie)-f? Is there some documentation where I can learn more about this? I don't even know what to search for to learn more about this kind of indexing.
My bad - I found it in the zsh manual here. It's explained in section 5.4.2 Using Associative Arrays.
To explain, it seems like this is a part of parameter substitutions in zsh. I don't know if it applies to bash as well.
This allows you to use certain "parameters" to perform some functionalities.
The syntax is to include the parameters within parentheses and prefix the specific part of the object that you want to substitute, modify etc.
For example, taking opts in my question,
echo ${opts}
prints the values of the associative array.
We have the parameter k which signifies the keys and v which signifies values. This can be used as follows:
echo ${(k)opts} # print only the keys
echo ${(kv)opts} # print the keys and values
To answer the main part of my question, what does (ie)-f mean, these are parameters that apply to the index of the associative array. Looking at the manual I had linked to, here is what i does - it searches for the key and returns the key instead of the value.
Explanation from the manual:
If instead of an ordinary subscript you use a subscript preceded by the flag (i), the shell will search for a matching key (not value) with the pattern given and return that. This is deliberately the same as searching an ordinary array to get its key (which in that case is just a number, the index), but note this time it doesn't match on the value, it really does match, as well as return, the key
And with regards to e - this seems a bit more complicated. But reading through the manual, it seems like this further evaluates the value if necessary i.e. in the event that it's not a constant.
Here is an example:
bar=4
foo='$bar'
> echo $foo
$bar
> echo ${(e)foo}
4
So combining the two together (ie) in my question returns the key and also expands it if necessary.

ZSH Completion Delimiter

I ran into the following situation. I got autocomplete entries like those:
server:install
server:lint
...
Those should be completable by zsh. Actually my completion method works kind of nice, as long as I replace the colons by underscores or something different, as ZSH (or better the _describe function) will use colons as delimiter.
You can see this in this example:
local -a subcmds topics
subcmds=('c:description for c command' 'd:description for d command')
topics=('e:description for e help topic' 'f:description for f help topic')
_describe 'command' subcmds -- topics
However, that's not what I want. I actually want to have those colons in the autocomplete entries.
So my question is: Can I either escape the colon somehow so that ZSH will note that my colon is not a delimiter for a description or change the delimiter to something different than a colon?
Currently the completion looks like this:
#compdef mycmd
local -a completions
completions=$(complete_me 2>/dev/null)
result=$(echo "$completions" | sed "s/:/_/g") # This line should be removed
lines=("${(f)result}")
_describe 'command' lines
All of this stuff is more or less copied together from various sites. So probably I just missed quite a simple option - but I just cannot find it -.-.
Can someone help me with this?
Thanks,
Matthias
Grml - it was an easy answer. _describe (as the name says) always expects a description. Replacing it by compadd just works:
compadd $lines
Maybe more people are as stupid as me :D - so I'll leave it here...

bad set of key/value pairs for associative array for custom completion

I am trying to follow Make zsh complete arguments from a file but with a command. Shouldn't matter much.
In /usr/local/share/zsh/site-functions/_foo I have
#compdef foo
aliases=($(a complicated command here printing everything twice))
_describe aliases aliases
The command when ran alone prints this:
foo foo bar bar this this that that
This seems to create an associative array just fine. When I add echo ${(kv)aliases} before the _describe aliases aliases command and run /usr/local/share/zsh/site-functions/_foo I am getting
foo foo bar bar this this that that
And when I just do ${(k)aliases} then
foo bar this that
I do not really a description so this would work for me. But, it doesn't work for zsh.
I added
function foo() { echo $* }
autoload _foo
compdef _foo foo
to ~/.zshrc and after . ~/.zshrc when I type foo [tab] I get _foo:2: bad set of key/value pairs for associative array. I tried changing the command to only print everything once. Same results. I tried changing the command to print "foo:x " for every foo. Same results. So what about should my program produce for this to work?
aliases already exists as an associative array containing all your shell aliases. Call the variable something else.
It may alias help to declare your variable local, for a normal array:
local -a compl_aliases
The bad set of key/value pairs usually indicates that you have an odd number of elements when doing an associative array assignment. There needs to be an even number with a value for each key.

Changing the global “path” from within a function?

My zshenv file has a bunch of lines like
if [[ -d "$HOME/bin" ]]; then
path=($HOME/bin $path)
fi
I thought I’d try to factor this pattern out into a function. I replaced it with
function prepend_to_path_if_exists() {
if [[ -d $1 ]]; then
path=($1 $path)
fi
}
prepend_to_path_if_exists("$HOME/bin")
but this gives the error
/Users/bdesham/.zshenv:8: missing end of string
where line 8 is the one where I’m calling prepend_to_path_if_exists. What exactly is causing this error, and how can I make this function work? I’m using zsh 5.0.5 on OS X 10.10.1.
You could call functions as with usual command executions like this (without ()):
prepend_to_path_if_exists "$HOME/bin"
It seems that zsh try to expand the glob prepend_to_path_if_exists(…) rather than to call the function.
TL;DR: Prepending emelemnts to $path would be accomplished by a little cryptic way:
(I'm not quite sure that the below form is preferable for anyone though.)
# `typeset -U` uniqify the elements of array.
# It could be good for $path.
typeset -U path
# prepending some paths unconditionally,
path[1,0]=(\
$HOME/bin \
$HOME/sbin \
)
# then filtering out unnecessary entries afterward.
path=(${^path}(-/N))
The $path[x,0]=… is prepending(splicing) element(s) to array taken from the below:
So that's the same as VAR[1,0]=(...) ? It doesn't really "look" very
much like prepend to me.
-- Greg Klanderman (http://www.zsh.org/mla/workers/2013/msg00031.html)
The ${^path}(-/N) expands the glob qualifires -/N on the each $path elements.
(Without ^ in the parameter expansion, the last elements of array will be evaluated, so it is mandatory in this case.)
The glob qualifires -/N means that "symbolic links and the files they point to"(-) the "directory"(/). And when it does not match anything do not raise errors (N).
In short, it would keep exsisting directories only for $path.

How can I remove a path from $PATH in Zsh and add it to the beginning without duplication?

I have:
PATH=/bar:/foo
I want:
PATH=/foo:/bar
I don't want:
PATH=/foo:/bar:foo
So I'm thinking, given the default path is PATH=/bar, I can modify $path (which is $PATH as an associative array):
function prepend_to_path() {
unset $path[(r)$1]
path=($1 $path)
}
prepend_to_path /foo
But that complains with:
prepend_to_path:unset:1: not enough arguments
It's been so long that I don't even remember what (r) is for, but without it (unset $path[$1]) I get:
prepend_to_path:1: bad math expression: operand expected at `/home/nerd...'
What am I doing wrong?
You can replace the body of your function with:
path=($1 ${(#)path:#$1})
Related answer: https://stackoverflow.com/a/3435429/1107999
This also works (and is arguably easier to read when you go back to it after a couple of months):
prepend_to_path () {
path[1,0]=$1
typeset -U path
}
typeset -U will automatically deduplicate the array, keeping only the first occurrence of each element.
Since export is equivalent to typeset -gx, you could also export -U path to kill two birds with one stone.
Edit: typeset -U needs only to be applied to a particular array once, so one can do that somewhere in one's shell startup and remove the line from the function above.

Resources