Square brackets in zsh completion function argument description, possible? - zsh

Basically, I'm wondering if its possible to do this:
#compdef foo
_arguments \
'--arg=[Description of --arg [With square brackets in the string!]]' \
without getting an invalid option error due to the nested square brackets?
I've tried all manner of escape characters. Single vs. double quotes makes no difference.

Try to escaping them like this:
'--arg=\\[Description of --arg \\[With square brackets in the string!\\]\\]'

Related

Avoid variable expansion in zsh

If I use the zsh shell and execute the following command I get
$zsh
$echo '$_GET["test"]'
preexec: bad math expression: operand expected at `"test"'
$echo '$_GET[]'
preexec: invalid subscript
In bash I get what I expect:
$bash
$echo '$_GET["test"]'
$_GET["test"]
I assume that zsh is trying to expand the $_GET variable. How can I avoid this? I always expected this to only happen within double quotes anyhow.
[update]
I found the following three lines in the .zshrc:
# Display last command interminal
echo -en "\e]2;Parrot Terminal\a"
preexec () { print -Pn "\e]0;$1 - Parrot Terminal\a" }
After commenting them out everything seems to work as expected.
What I understand is that preexec is executed after a command in the terminal has been submitted but before it is executed. The $1 is the command that one submitted.
I still do not understand the purpose of the two lines but is it because of the double quotes in the preexec print statement that the variables are expanded?
The combination of print -P together with the expansion of $1 is killing you. With this, you first get a "normal" expansion of $1, yielding something like "\e]0;echo '$_GET["test"]'...". Now -P causes print to do a prompt expansion on this string, which means that it has to expand $_GET["test"] as well. This causes the error.
I suggest to remove the -P, in particular since you don't have any characters in your string which would benefit from prompt expansion.

Zsh: string getting expanded

I am trying to create a Zsh version of the Command Line Window in Vim.
I want to use the moreutils program vipe to pipe history into.
For that purpose, I have something like:
EDITOR='nvim -c "normal G"'
fc -ln | vipe
Here, fc -ln represents the history, and $EDITOR represents the program that I'll be piping into.
The problem is, the above does not work.
In this specific case I get the file G" opened. It seems that the double quotes to surround the command are not being recognized.
Nor could I get it to work with any other combination of single quotes, double quotes or variables.
How can I pass in the string "normal G"?
vipe splits its arguments by space, so it is not possible to use multi-word c options. I resolved this by creating a .vim file and using nvim -S file.vim.

Sort list of files in different directories with zsh

Assume I have the following directory/file structure
dirA/1fileAA.zsh
dirA/99fileAB.zsh
dirB/2fileBA.zsh
dirB/50fileBB.zsh
dirB/subdirA/20fileBAA.zsh
which I want to have ordered by the numbers the filenames begin with, ignoring any directories, so I get
dirA/1fileAA.zsh
dirB/2fileBA.zsh
dirB/subdirA/20fileBAA.zsh
dirA/99fileAB.zsh
dirB/50fileBB.zsh
using just built-in zsh functionality.
What would be the best way to achieve this?
I could think of rewriting strings sort and write them back?
Or better try to create an associated array and sort by keys?
I'm still a zsh and want to avoid digging into the wrong direction, too much.
Here is one way to accomplish this using only zsh builtins. The function prepends the filename to the front of each path for sorting and then removes it:
function sortByFilename {
local -a ary
printf -v ary '%s/%s' ${${argv:t}:^argv}
print -l ${${(n)ary}#*/}
}
With your example directory setup, it can be invoked from the parent directory of dirA and dirB with:
sortByFilename **/*.zsh
Testing it:
sortByFilename \
dirA/1fileAA.zsh \
dirA/99fileAB.zsh \
dirB/2fileBA.zsh \
dirB/50fileBB.zsh \
'/leadslash/42 and spaces' \
dirB/subdirA/20fileBAA.zsh
Result:
dirA/1fileAA.zsh
dirB/2fileBA.zsh
dirB/subdirA/20fileBAA.zsh
/leadslash/42 and spaces
dirB/50fileBB.zsh
dirA/99fileAB.zsh
The pieces:
printf -v ary <fmt> ...: runs printf with the format string, and assign the results to the ary array. Each iteration of the format string will become another element in the array.
%s/%s: the format string. This will concatenate two strings with a slash separator.
If there are more values than in the input than specifiers in the format string, printf will repeat the format pattern. So here, it will pull pairs (of filename/pathname) from the input array.
${${argv:t}:^argv}: this will produce an array alternating with filenames and full paths, i.e. (file1 path1 file2 path2 ...)
${ :^ }: zsh parameter expansion that will zip two arrays to create the alternating filenames and paths.
${argv:t}: array of filenames. Built using the function positional parameters in argv, and the :t modifier, which returns the filename component for each element in the array.
argv: array of full paths.
print -l: print each element of the input on a separate line.
${${(n)ary}#*/}: the final sorted list of paths.
${(n)ary}: Returns the array sorted numerically, using the n parameter expansion flag. At this point, each element in ary is the concatenation of the filename, a slash, and the input path.
The n flag works here because of the filename pattern; it will sort by decimal value instead of lexically within a common / empty prefix, e.g. foo1 foo3 foo12.
${ #*/}: Removes the pattern */ from the front of each element in the array. This deletes the prefix that was being used for sorting, leaving the original path.
local -a ary: declares an array variable. This is used as an indicator to printf -v to split its output.
It's possible to eliminate this line and make the function shorter and a bit more cryptic by (re-/mis-/ab)using the pre-declared array argv.
function sortByFilename {
printf -v argv %s/%s ${${argv:t}:^argv}
print -l ${${(n)argv}#*/}
}
Edit - a single-line version:
(){print -l ${"${(n0)$(printf '%s/%s\0' ${${argv:t}:^argv})}"#*/}} **/*.zsh
Including this simply because one-liners are fun to create, not because it's recommended. With the anonymous function, process substitution, and additional parameter expansion flags, this is less readable and possibly less efficient than the function above.

jq doesn't work with keys which contains dash in it from a variable

If you'd like jq to escape dashes, you need to put your key between square brackets like this;
jq '.["key-key"]'
and apart from that, if you'd like to include a variable in jq, you need to use double quotes instead of single quotes;
jq "."${var[i+1]}""
but my variable contains dash in it and in this case, I've tried to merge the 2 examples above but it didn't work;
var=key-key
jq ".["${var[i+1]}"]."key""
how can I get this work?
Update:
This is the final script, which I've forgot to mention;
declare -a var=(
"key-key"
"key2-key2"
"key3-key3"
)
for ((i=0; i<${#var[#]})); do
curl -s "url" | jq ".["${var}"]."something""
done
To have double-quotes in a jq command you've enclosed in double-quotes, you'd escape them with a backslash :
jq ".[\"key-key\"]"
Another problem with your final command is that ${var[i+1]} expands to the empty string, because this syntax is used to index elements of an array, and you previously defined var as a simple string.
A better way to work with variables in jq commands is to define them through the --arg name value option, after which you can refer to them with $foo in a single-quotes enclosed command :
jq --arg keyName key-key '.[$keyName]'
To fix the code included in the update, I would use the following :
declare -a var=(
"key-key"
"key2-key2"
"key3-key3"
)
json=$(curl -s "url")
for searchedKey in "${var[#]}"; do
echo $json | jq --arg keyName $searchedKey '.[$keyName].something'
done

Mix of parameter and command substitutions

I found this snippet of code (simplified)
while read item; do
echo -n "${(q)item} "
done
from here https://github.com/junegunn/fzf/blob/master/shell/key-bindings.zsh#L12
I don't understand the expression "${(q)item} ".
What is variable q, I didn't find any declaration of it, is it a command substitution? Why parentheses use inside curly braces? What is meaning of this construction?
Parentheses immediately after ${ specify parameter expansion flags. The q flag is used to quote special characters in the expansion.
Quote characters that are special to the shell in the resulting words with backslashes; unprintable or invalid characters are quoted using the $'\NNN' form, with separate quotes for each octet.

Resources