zsh - bad substitution for echo - zsh

on macOS with it's zsh - the below script (snippet) did work with bash. I have two text files, the first one that keeps a variable of menu file we are working with now, the other one, that can be mane of them - showing the menu text (a1=Some menu description item) , and the command line (c1=Some command to run).
The following shell script:
CommandDesc="a$1"
CommandString="c$1" # The Command string ie. cmd3
case "$1" in
"")
for (( i=0; i<=$ans_menu_count; i++ ))
do
#eval var1="ans"$i"_opt" #creating a var ans1_opt
eval desc="a"$i #Creating a var ans1_desc
#echo "${!var1} : ${!var2}"
echo "$i : ${!desc}"
done ;
(The full script can be found here, above just a snippet: https://github.com/IoTPlay/menu_ansible/tree/master/ans-menu)
the above script, working with bash, produces an error bad substitution at the line echo "$i : ${!desc}" with zsh.
I read the other articles - but cannot see how to fix it. Any guidance please?

Got it working, thank you, main culprit was making a "variable from a variable" like so:
${(P)CommandString}
and a few small other thingies that zsh does differently than bash.
Full working menu system here: https://github.com/IoTPlay/menu_ansible/blob/master/ans-menu/ans.zsh

Related

ZSH: prompt expansion return code greater than

The problem is (theoretically) simple. All I want is for my zsh prompt to print the return code if it is less than or equal to 128 and the corresponding signal when greater than 128. I cannot find any example of this being done and the zsh docs only specify how to do it
if the exit status of the last command was n
The only version I have got (somewhat) working is the following (which only works for SIGINT):
PROMPT='%130(?.[$(kill -l $?)].$?)> '
I have also tried using precmd but completely failed with that (it appears the return code is interfered with when zsh is executing the function but don't quote me on that).
The solution was indeed simple and just involved creating a different function (to which I passed the return code) rather than using precmd. Below is the final version of my zsh prompt, including the return code / signal behaviour:
code () {
if (( $1 > 128 )); then
echo "SIG$(kill -l $1)"
else
echo $1
fi
}
setopt promptsubst
PROMPT='%F{green}%n%f#%m %F{cyan}%~%f> '
RPROMPT='%(?..%F{red}[$(code $?)]%f'

Using tput with make

I am looking to do some formatting of the output from my makefile using tput. An example: if you simply type
echo $(printf '%*s' "${COLUMNS:-$(tput cols)}" '' | tr ' ' –)
as a command in your shell it will output a nice line spanning the entire width of your terminal window.
I am wondering if there's any way to carry this over in a makefile? The following only produces a blank line:
lineTest:
#echo $$( printf '%*s' "${COLUMNS:-$(tput cols)}" '' | tr ' ' – )
Definitely a silly question, but please do chime in if you happen to know.
You have to escape ALL the $ you want to pass to make. You only escaped the first one. Also I don't know why you're invoking printf in a subshell then echoing the results...??
This works for me:
lineTest:
#printf '%*s\n' "$${COLUMNS:-$$(tput cols)}" '' | tr ' ' -
I should point out, this won't work reliably if you invoke make with parallel builds enabled, because in parallel mode not all of the jobs get access to the terminal.

zsh function only runs once

I'm fairly new to zsh (oh-my-zsh) and i'm trying to write a custom theme.
I ran into a problem and reduced it to the following testcase
PROMPT='$RANDOM > '
works as expected, it produces a random number on each command.
But when using a function
PROMPT='$(my_random) > '
function my_random(){
echo $RANDOM
}
it always returns the same number, even after source ~/.zshrc still the same number. only when i close the terminal window and open it again, i get a new number which stays the same for the complete session.
only when i do:
PROMPT='$RANDOM $(my_random) > '
function my_random(){
echo $RANDOM
}
i get two random numbers as expected... any explanation for this behaviour?
btw, i'm using kde's konsole on a fresh arch install.
Edit
fwiw i found using /dev/urandom directly works well. I would still like to know whats going on.
function my_random() {
echo $(cat /dev/urandom | tr -dc '0-9' | head -c5)
}
$()-expansion happens in a subshell, and changes to $RANDOM in a subshell don’t affect the parent. From zshparam(1):
The values of RANDOM form an intentionally-repeatable pseudo-random sequence; subshells that reference RANDOM will result in identical pseudo-random values unless the value of RANDOM is referenced or seeded in the parent shell in between subshell invocations.
You don’t need to turn to setting the prompt to reproduce it:
% echo $(echo $RANDOM)
17454
% echo $(echo $RANDOM)
17454
bash doesn’t share zsh’s behaviour here.
The annoying bit is that prompt expansion also happens in a subshell, so you can’t just fix this by referencing $RANDOM in, say, precmd. The best way I can find is to do it in an empty expansion:
PROMPT='${RANDOM##*}$(my_random) > '
As said in a comment by chepner, you can fix this by putting : $RANDOM; in your precmd. This causes the value of $RANDOM to be taken and a new one to be generated.
e.g.
precmd() {
: $RANDOM;
...
}

zsh - iterate over files matched using parameter in the script

I have this code executed fine and correctly in the zsh shell:
for f in ./A*.html; do; echo $f; done
output
./Aliasing.html
./Alternate-Forms-For-Complex-Commands.html
./Alternative-Completion.html
./Arguments.html
./Arithmetic-Evaluation.html
./Arithmetic-Expansion.html
./Array-Parameters.html
./Author.html
./Availability.html
However when I use this code, but sending a matching string (./A*.html) as a parameter in the zsh function, it will display only the first file
script:
displayy() {
for f in $1; do; echo $f; done
}
command:
%displayy ./A*.html
output
./Aliasing.html
I would rather expect the same files are printed out as when executing for loop in shell (first example). Any ideas what I'm doing wrong? Grazie
The problem with displayy ./A*.html command is that * is expanded by zsh before it is passed to the dispayy function. So in fact your command looks like this:
$ displayy ./Aliasing.html ./Alternate-Forms-For-Complex-Commands.html ...
Then in displayy you print just first argument: ./Aliasing.html.
The easiest way to solve this issue is to change one char (1=>#) in you displayy definition:
displayy() {
for f in "$#"; do; echo "$f"; done
}
This way iteration of loop goes through all display arguments. Additionally I recommend to put double quotes around variables as a good habit, even if in this example there are no whitespaces in file names.

ifeq issue: compare 2 strings with a dot included

I am trying to implement a simple string comparison to get the type of a file (using its extension) like this:
extract_pkg: $(PKG)
$(eval EXT := $(suffix $(PKG)))
#echo $(EXT)
ifeq ($(EXT), .zip)
#echo "is zip file"
else
#echo "is not a zip file"
endif
extract_pkg : PKG = mypkg.zip
However, when I run it it goes into the else branch. My guess is, it has to do with the dot, but I dont find a solution. Thanks for your help !
Edit 1: the essential code would be actually somewhat like the following, and it works as expected:
test_cmp:
ifeq (.zip,.zip)
#echo ".zip==.zip"
endif
ifeq (zip,zip)
#echo "zip==zip"
endif
thus the problem is somewhere else !
One thing to be careful about -- spaces in if constructs are significant. So if you have something like:
ifeq ($(EXT), .zip)
it will only match if $(EXT) expands to exactly ".zip" -- including the space before the period. So your first example will always print is not a zip file, since $(EXT) will never contain the space.
You cannot use ifeq() etc. inside recipes. ifeq() are preprocessor statements: they are interpreted immediately as the makefile is read in. Recipes are not run until much later, after all makefiles are parsed and make decides that this target needs to be updated. So trying to set a variable in a recipe using eval, etc. then test that variable using ifeq() cannot work.
You have to use shell constructs for this; something like:
extract_pkg: $(PKG)
#EXT=$(suffix $<); \
echo $$EXT; \
if [ $$EXT = .zip ]; then \
echo "is zip file"; \
else \
echo "is not a zip file"; \
fi

Resources