Skip to content
Advertisement

Shell: Pass function with arguments as function argument

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,)

User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement