Get name of calling function in zsh - 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.

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.

zsh completion complete literal quotes

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!

How can I access the current value of an environment variable in a zsh prexec function?

I have a simple function like
function clearit() {
REM=$(($LINENO % $LINES))
DIV=$(($LINENO / $LINES))
if [[ $DIV -gt 0 && $REM -lt 3 && $DIV ]]; then
clear
fi
echo $LINENO, $LINES
}
add-zsh-hook preexec clearit
In the above function a static value of $LINE and $LINES is added to the prehook function. I want to get the current value when the prexec is executed. How can I do that ?
For normal shell variables, you will get the current value:
% x=1
% function test_hook() { echo $x; }
% add-zsh-hook preexec test_hook
% true
1
% x=2
1
% true
2
However, $LINENO is a very special variable. From the documentation:
LINENO <S>
The line number of the current line within the current script, sourced file, or shell function being executed, whichever was started most recently. Note that in the case of shell functions the line number refers to the function as it appeared in the original definition, not necessarily as displayed by the functions builtin.
When your hook function is executing, it is by definition the most recently started shell function, script or sourced file. So $LINENO always gives you the current line within your hook function. There is no way to access the $LINENO for the outer scope.
However, even if you could access the outer $LINENO in your hook, it would lead to very strange behaviour in my opinion. Let's say your terminal is 30 lines high ($LINES is 30), it would clear the terminal before executing the 30th, 31st and 32nd command, then nothing for the next 27 commands, then again clearing before the 60th, 61st and 62nd, and so on. I don't think this is remotely what you are trying to achieve...

Is there an API for grep, pipe, cat in groovy?

Is there an API for grep, pipe, cat in groovy?
Not sure I understand your question.
Do you mean make system calls and pipe the results?
If so, you can just do something like:
println 'cat /Users/tim_yates/.bash_profile'.execute().text
To print the contents of a file
You can pipe process output as well:
def proc = 'cat /Users/tim_yates/.bash_profile'.execute() | 'grep git'.execute()
println proc.text
If you want to get the text of a File using standard Groovy API calls, you can do:
println new File( '/Users/tim_yates/.bash_profile' ).text
And this gets a list of the lines in a file, finds all that contain the word git then prints each one out in turn:
new File( '/Users/tim_yates/.bash_profile' ).text.tokenize( '\n' ).findAll {
it.contains 'git'
}.each {
println it
}

Resources