I’m working on a shell program to automatise my Arch (mandatory btw) installation. To make it more interactive, I’ve built the following function:
# READYN # ARGS: # - Yes/no question # - Command to run if yes # - Command to run if no # # Prompts the user with a yes/no question (with precedence for yes) and # run an order if the answer is yes or another if it's no. readyn () { while : do local yn; printf "%s? [Y/n]: " "$1"; read yn; if [[ "$yn" =~ ^([yY][eE][sS]|[yY])?$ ]]; then $2; break; elif [[ "$yn" =~ ^([nN][oO]|[nN])+$ ]]; then $3; break; fi done }
I’ve succeeded in passing an "echo Hello World!"
as an argument and having it run. I’ve also been able to pass another function. For example:
yayprompt () { printf "yay is required to install %s.n" "$1" readyn "Install yay, the AUR manager" "yayinstall" "" }
This calls yayinstall
if yes and does nothing if no.
My problem comes with more complex functions, which are passed as arguments but are either not recognised or run when they’re not supposed to. The problem comes with the following function:
# MANAGEPGK # ARGS: # - Package name # - Package variable # - Yay required # # Checks if the package is added to the pkglist to either add or remove it. # If yay is required to install it, it prompts the user whether they wish # to install yay or don't install the package (if yay is not installed). # This functions DOES NOT prompt any installation options on the user. To # do this, use PROMPTPKG. managepkg () { local pkgvar=$2 if [ $pkgvar == 0 ]; then if [ $3 == 1 ] && [ $yay == 0 ]; then yayprompt; fi if [ $3 == 0 ] || [ $yay == 1 ]; then addpkg "$1"; pkgvar=1; fi else rmpkg "$1"; pkgvar=0; fi echo "$pkgvar"; }
For it to work properly, it has to (or at least I’ve had to) be called like this:
dewm_cinnamon=$(managepkg cinnamon $dewm_cinnamon 0)
Now, I’m trying to pass it as an argument to readyn
, but I’m having these outputs depending on the format (I’m always answering yes
as empty string:
Simple quotes:
readyn "Install gaps" 'dewm_i3gaps=$(managepkg i3-gaps $dewm_i3gaps 0)' 'dewm_i3=$(managepkg i3-wm $dewm_i3 0)';
Install gaps? [Y/n]: ./architup.sh: line 341: dewm_i3gaps=$(managepkg: command not found
Double quotes:
readyn "Install gaps" "dewm_i3gaps=$(managepkg i3-gaps $dewm_i3gaps 0)" "dewm_i3=$(managepkg i3-wm $dewm_i3 0)";
Install gaps? [Y/n]: ./architup.sh: line 341: dewm_i3gaps=1: command not found
Dollar enclosed: (This one runs both commands as seen in cat pkglist
)
readyn "Install gaps" $(dewm_i3gaps=$(managepkg i3-gaps $dewm_i3gaps 0)) $(dewm_i3=$(managepkg i3-wm $dewm_i3 0));
Install gaps? [Y/n]: Install compton? [Y/n]: ^C Documents/Repositories/architup took 5s ➜ cat pkglist i3-gaps i3-wm
What syntax should I use to have readyn
run only one command based on the user input?
Thank you!
Advertisement
Answer
Function arguments are just strings. A better design IMHO is to simply have readyn
return true (zero) for “yes” and false otherwise, and have the calling code implement any conditional logic based on that.
readyn () { read -p "$@" case $REPLY in [Yy] | [Yy][Ee][Ss]) return 0;; esac return 1 } readyn "Are you ready San Antonio?" && rock and roll if readyn "Let me hear you say yeah"; then echo "Let's go!" else echo "If you feel mellow, get outta here" fi
(With apologies to rock concerts everywhere,)