Command validations with ZSH

equiman

Camilo Martinez

Posted on March 8, 2021

Command validations with ZSH

I'm tired of forgetting to run some commands before and others after a process. So I decided to give this responsibility to the terminal. Why not?

In ZSH we have two hooks preexec (after) and precmd (before) that can be used to achieve this task.

Validations

We need two variables, one to know if the process was quite early to not run the precmd (before) hook and another to know which command is running actually.

Open ~/.zshrc file and add these variables to the script.

local quit="y"
local cmd=""
Enter fullscreen mode Exit fullscreen mode

I always forgot to run npm run build command before npm publish. So we are going to create a function before_validation to show a message to confirm if we had run the command before.

cancel(){
  echo "\e[43m\e[30mALERT:\e[0m Did you run \e[1m'$1'\e[0m command before?"
  echo "\e[32m[ANY] = Continue \e[0m| \e[31m[Ctrl+c] = Cancel \e[0m"
  read -sk key
  quit="n"
}

before_validation(){
  local cmd_validation=""
  local cmd_previous=$(fc -ln -1 | xargs) # Get previous command from history
  local cmd_current=$(echo $1 | xargs)

  if [[ "$cmd_current" =~ ^"npm publish" ]]; then
    cmd_validation="npm run build"
  elif [[ "$cmd_current" =~ ^"git checkout" ]]; then
    cmd_validation="git pull"
  # ---
  # Add all elif (else if) that you need
  elif [[ "$cmd_current" =~ ^"your command" ]]; then
    cmd_validation="command to run before"
  # ---
  fi

  if [[ ! -z $cmd_validation ]]; then
    if [[ "${cmd_validation}" != "${cmd_previous}" ]]; then
      cancel $cmd_validation # show cancel alert if is not the previous
    fi
  else
    quit="n"
  fi
}
Enter fullscreen mode Exit fullscreen mode

Currently, there is no warning about dangerous commands. So we are going to create a function danger_validation to display a warning message and give us a second chance.

danger(){
  echo "\e[41m\e[97mDANGER:\e[0m Are you sure? really?"
  echo "\e[32m[ANY] = Continue \e[0m| \e[31m[Ctrl+c] = Cancel \e[0m"
  read -sk key
  quit="n"
}

danger_validation(){
  local cmd_current=$(echo $1 | xargs)

  if [[ "$cmd_current" =~ ^"rm -rf /" ]]; then
    danger
  elif [[ "$cmd_current" =~ ^"git reset --hard HEAD" ]]; then
    danger
  elif [[ "$cmd_current" =~ ^"git clean -f -d -x" ]]; then
    danger
  # Add all elif (else if) that you need
  elif [[ "$cmd_current" =~ ^"your command" ]]; then
    danger
  # ---
  fi
}
Enter fullscreen mode Exit fullscreen mode

And I also forgot to run npm i command after git pull origin .... So we are going to create a function after_validation to show a message to confirm if we want to run a command after.

run(){
  echo "\e[43m\e[30mALERT:\e[0m Do you want to run \e[1m'$1'\e[0m command after?"
  echo "\e[32m[Y] = Yes \e[0m| \e[31m[ANY] = Cancel \e[0m"
  read -sk key
  if [[ "$key" == "y" ]] || [[ "$key" == "Y" ]]; then
    echo "\e[32m❯ \e[33mRunning...\e[0m"
    eval $1
  fi
}

after_validation(){
  if [[ "$cmd" =~ ^"git pull origin" ]]; then
    run "npm i"
  elif [[ "$cmd" =~ ^"git checkout" ]]; then
    run "npm i"
  elif [[ "$cmd" =~ ^"npm run build" ]]; then
    run "obp" #open build folder alias
  # ---
  # Add all elif (else if) that you need
  elif [[ "$1" =~ ^"your command" ]]; then
    cancel "command to run after"
  # ---
  fi
}
Enter fullscreen mode Exit fullscreen mode

Now we need to create, something like a "factory function" that can be associated with the hooks. I preferred this way because we can call more than one function before or after.

pre_validation() {
  quit="y" && cmd=""
  [[ $# -eq 0 ]] && return # If there's no input, return. Else...
  cmd="$1" # Save global for after validation
  expand_command_line "$@"
  danger_validation "$@"
  before_validation "$@"
}

pos_validation() {
  [[ -z $cmd ]] && return # If there's no cmd, return. Else...
  if [[ "$quit" == "n" ]]; then
    after_validation
  fi
  quit="y"
}
Enter fullscreen mode Exit fullscreen mode

And here, we are going to associate pre_validation function with preexec (before) hook and pos_validation with precmd (after) hook.

autoload -U add-zsh-hook                  # Load the zsh hook module
add-zsh-hook preexec pre_validation       # Adds the pre hook
add-zsh-hook precmd pos_validation        # Adds the pos hook
Enter fullscreen mode Exit fullscreen mode

I'm not planning to remove these hooks, but you can do it with these commands:

# add-zsh-hook -d preexec pre_validation  # Remove it for this hook
# add-zsh-hook -d precmd pos_validation   # Remove it for this hook
Enter fullscreen mode Exit fullscreen mode

Once finish, reopen all terminals or update his source running source ~/.zshrc command and now you are ready to use validations.


Example

danger

pre

pos


You can download or clone this code and other ZSH utilities from GitHub: dot Files repository.


That’s All Folks!
Happy Coding 🖖

beer

Sources

💖 💪 🙅 🚩
equiman
Camilo Martinez

Posted on March 8, 2021

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related

Open File Explorer and Browser from ZSH
productivity Open File Explorer and Browser from ZSH

March 9, 2021

Command validations with ZSH
productivity Command validations with ZSH

March 8, 2021

Useful Alias for ZSH
productivity Useful Alias for ZSH

February 23, 2021