DEV Community

Cover image for Command validations with ZSH
Camilo Martinez
Camilo Martinez

Posted on • Edited on

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

Top comments (1)

Collapse
 
nahoj profile image
Johan Grande • Edited

Nice! This inspired me to put the following in my .zshrc, for all the times I want to know the contents of a variable or what the result of an expansion would be, and I find it tedious to type echo or I outright forget. Now I don't have to anymore. Similar to the auto_cd option.

autoload -U add-zsh-hook auto_print(){ if [[ $1 = '$'* ]]; then eval "print -r -- $3" read -sk key print -r "[ANY] = Execute as a command | [Ctrl+c] = Cancel" read -sk key fi } add-zsh-hook preexec auto_print 
Enter fullscreen mode Exit fullscreen mode

Which gives:

% x=42 % $x # Enter + Ctrl-c 42 % $x # Enter + Enter + Ctrl-c 42 [ANY] = Execute as a command | [Ctrl+c] = Cancel % $x # Enter + Enter + Enter 42 [ANY] = Execute as a command | [Ctrl+c] = Cancel zsh: command not found: 42 % $((10 + 2 ** 5)) 42 
Enter fullscreen mode Exit fullscreen mode