zsh completion complete literal quotes - zsh

I am writing my own completions for a program.
I would like to be able to complete quoted words, maintaining the double or single quotes in the completion.
#compdef foo
_foo {
local strings
strings=(\
foo\
bar\
'spam eggs')
_arguments \
{-s,--string}'[Select a string]:STR:(\""${strings[#]}"\")\
&& return 0
}
_foo
what I'd expect:
foo -s <TAB>
"foo" "bar" "spam eggs"
what it get:
\"foo\" \"bar\" \"spam\ eggs\"
I ended up trying different combinations of nested quotes and escapes almost brainlessly but with no luck, as I was not able to find the relevant docs (really, zsh docs are "dense")
Thank you!

Related

bjam - cannot assign a literal to a variable?

Well, this must be the most stupid and idiotic behavior I've seen from a programming language.
https://www.bfgroup.xyz/b2/manual/release/index.html says:
Syntactically, a Boost.Jam program consists of two kinds of
elements—keywords (which have a special meaning to Boost.Jam) and
literals. Consider this code:
a = b ;
which assigns the value b to the variable a. Here, = and ; are
keywords, while a and b are literals.
⚠ All syntax elements, even
keywords, must be separated by spaces. For example, omitting the space
character before ; will lead to a syntax error.
If you want to use a literal value that is the same as some keyword,
the value can be quoted:
a = "=" ;
OK, so far so good. So I have this in my Jamroot:
import path : basename ;
actions make_mytest_install
{
echo "make_mytest_install: MY_ROOT_PATH $(MY_ROOT_PATH) PWD $(PWD:E=not_set)" ;
epath = "$(MY_ROOT_PATH)/projects/mytest/bin/gcc-9/release/qt5client" ;
ename = basename ( $(epath) ) ;
echo "epath $(epath) ename $(ename)" ;
}
explicit install-gettext ;
make install-mytest : : #make_mytest_install ;
... and I try this:
bjam install-mytest
...updating 1 target...
Jamfile</home/USER/src/myproject>.make_mytest_install bin/install-mytest
make_mytest_install: MY_ROOT_PATH /home/USER/src/myproject PWD not_set
[ SHELL pstree -s -p 2720269 && echo PID 2720269 PWD /home/USER/src/myproject ]
/bin/sh: 13: epath: not found
/bin/sh: 14: Syntax error: "(" unexpected
.....
...failed Jamfile</home/USER/src/myproject>.make_mytest_install bin/install-mytest...
...failed updating 1 target...
Now - how come that the SIMPLEST assignment to a string, EXACTLY AS in the manual:
epath = "$(MY_ROOT_PATH)/projects/mytest/bin/gcc-9/release/qt5client" ;
... fails, and this variable cannot be found anymore?
What is the logic in this? How the hell is this supposed to work? I would get it if MY_ROOT_PATH was undefined - but the echo before it, shows that it is not? What is this lunacy?
So I cannot believe I'm asking something this trivial, but:
How do you assign a string to a variable in bjam language?
Well, the error gives somewhat of a hint: /bin/sh: -> so apparently inside actions, it is sh that runs - then again, if it was really sh I could have assigned variables, but I can't. So best I could do, was to remove the assignments OUT of actions:
import path : basename ;
epath = "$(MY_ROOT_PATH)/projects/mytest/bin/gcc-9/release/qt5client" ;
# ename = basename ( $(epath) ) ; # nope, causes target install-mytest to not be found :(
# calling a shell for basename works - but adds a damn NEWLINE at end!?!?!?!
ename = [ SHELL "basename $(epath)" ] ;
actions make_mytest_install
{
echo "make_mytest_install: MY_ROOT_PATH $(MY_ROOT_PATH) PWD $(PWD:E=not_set)" ;
echo "epath $(epath) ename $(ename)" ;
}
explicit install-mytest ;
make install-mytest : : #make_mytest_install ;
So, assignment kind of passes, but you still can't get the basename ?!
I still don't understand, who thought this kind of variable management is a good idea ... I don't even understand, how people managed to build stuff with this system

How to initialize array from array in zsh?

My script test.zsh:
args=$#
argss=($#)
echo ${#:2}
echo ${args:2}
echo ${argss:2}
The output:
$ ./test.zsh foo bar foobar
bar foobar
o bar foobar
o
It looks like args is being initialized as the string of $# instead of as an array. How do I initialize args as an array? ($#) does not seem to work either
You need to put parentheses around $# to make args an array:
args=($#)
In other shells, you should also put quotes around it (args=("$#")) to avoid word splitting, but this is disabled by default in zsh (see the option SH_WORD_SPLIT).
Note that ${#:2} will give you $1 $2 $3 ..., while ${args:2} will give $2 $3 ..., because zsh prepends $0 to $# when you use that form of parameter subscripting for compatibility with other shells.
The preferred zsh way to subscript arrays is ${arr[start,end]}, where end is inclusive and may be negative.
${args[1,-1]} and ${args[#]} will expand to the same thing.

HP-UX KSH scripting - passing blank parameters with $#

I have a 'problem' with a script I'm developing in HP-UX KSH. The script contains many functions, and I need to pass the same set of parameters between them. All well and good, but some parameters can be blank. Its easy to pass blank parameters using double double-quotes (""), but what if I want to pass a complete set of parameters from one function into another using ${#}, including blanks? And to make things tricky, there can be a variable number of parameters each time, so the method has to be dynamic.
Example: I've got a function called test1 that takes a number of parameters. Any of them can be blank. I've also created a function called test2 into which all the parameters of test1 are passed:
test1()
{
echo 1-1: ${1}
echo 1-2: ${2}
test2 ${#}
}
test2()
{
echo 2-1: ${1}
echo 2-2: ${2}
}
# test1 "" hello
1-1:
1-2: hello
2-1: hello
2-2:
The trouble is, if ${1} is blank, ${2} from test1 appears as ${1} in test2. So to work around the problem I created this code, which effectively creates a function string with all parameters surrounded with double quotes:
test1()
{
typeset var FUNC="test2"
typeset -i var COUNT=1
echo 1-1: ${1}
echo 1-2: ${2}
while [ ${COUNT} -le ${##} ]; do
typeset var PARAM=$(eval "echo \$${COUNT}")
FUNC="${FUNC} \"${PARAM}\""
((COUNT=COUNT+1))
done
eval "${FUNC}"
}
# test1 "" hello
1-1:
1-2: hello
2-1:
2-2: hello
This works very nicely, thank you. Now to my 'problem'.
Is it actually possible to encapsulate the above code in a function of its own? It seems a catch 22 to me, in that you have to run that code to pass the blank parameters. I have to repeat this code snippet many times in my script because I can't find another way. Is there one?
Any help or guidance will be gratefully received.
Here is how I would write your functions:
show_params() {
typeset funcname=$1
typeset -i n=0
shift
for arg; do
((n++))
printf "%s:%d >%s<\n" "$funcname" $n "$arg"
done
}
test1() { show_params "${.sh.fun}" "$#"; test2 "$#"; }
test2() { show_params "${.sh.fun}" "$#"; }
test1 "" 'a string "with double quotes" in it'
test1:1 ><
test1:2 >a string "with double quotes" in it<
test2:1 ><
test2:2 >a string "with double quotes" in it<
Using your definition of test1, which builds up a string containing a command, adding double quotes around all the paramers, and then eval-ing the string, I get this result
$ test1 "" 'a string "with double quotes" in it'
1-1:
1-2: a string "with double quotes" in it
test2:1 ><
test2:2 >a string with<
test2:3 >double<
test2:4 >quotes in it<
That's because you're doing this:
eval "test2 \"\" \"a string \"with double quotes\" in it\""
# ......... A A A B B A
# A = injected quotes
# B = pre-existing quotes contained in the parameter

Get name of calling function in zsh

I want to get function caller name in shell script sometime, in bash it works with ${FUNCNAME[1]}
${FUNCNAME[1]} is a (caller name)
${FUNCNAME[0]} is c (current name)
but it not work in zsh
ie i want to know which function call me in function c
function a(){
c
}
function b(){
c
}
function c(){
#if a call me; then...
#if b call me; then...
}
The function call stack is in the variable $funcstack[].
$ f(){echo $funcstack[1];}
$ f
f
So in c the calling function (a or b) is $funcstack[2] or perhaps more conveniently $funcstack[-1].
Generic solution
Works whether array indexing starts at 0 (option KSH_ARRAYS) or 1 (default)
Works in both zsh and bash
# Print the name of the function calling me
function func_name () {
if [[ -n $BASH_VERSION ]]; then
printf "%s\n" "${FUNCNAME[1]}"
else # zsh
# Use offset:length as array indexing may start at 1 or 0
printf "%s\n" "${funcstack[#]:1:1}"
fi
}
Edge case
The difference between bash and zsh is that when calling this function from a sourced file, bash will say source while zsh will say the name of the file being sourced.

side effect of zsh echo?

For some reason this script will work with all of the 'echo's at the end, but without them $wall is an empty string. This seems like really odd behaviour.
#!/bin/zsh
if [ ! -n "$1" ] ; then
files=(~/pictures/backgrounds/*jpg)
else
while [ $1 ] ; do
files+=(`echo $1/*jpg`)
shift
done
fi
echo $files
N=${#files}
echo $N
((N=RANDOM%N))
echo $N
wall=${files[$N]}
echo $wall
cp $wall ~/wall.jpg
This code will sometimes fail because RANDOM%N can result in zero and zsh array indexes start with 1. You should use RANDOM%N+1 instead.
You can:
setopt ksharrays
to enable zero-based indexing.
From man zshoptions:
Emulate ksh array handling as closely as possible. If this
option is set, array elements are numbered from zero, an array
parameter without subscript refers to the first element instead
of the whole array, and braces are required to delimit a sub‐
script (${path[2]}' rather than just$path[2]').

Resources