Command validations with ZSH
Camilo Martinez
Posted on March 8, 2021
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=""
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
}
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
}
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
}
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"
}
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
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
Once finish, reopen all terminals or update his source running source ~/.zshrc
command and now you are ready to use validations.
Example
You can download or clone this code and other ZSH utilities from GitHub: dot Files repository.
That’s All Folks!
Happy Coding 🖖
Sources
Posted on March 8, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.