Oct 19, 2023 16 min read

How to Use Editors, Regex, and Hooks with Z-shell

Use editors, regex and hooks with z-shell with our step-by-step tutorial. A powerful and feature-rich shell for Unix-like operating systems.

Use Editors, Regex, and Hooks with Z-shell
Table of Contents

Introduction

Before we start talking about how to use editors,regex and hooks with z-shell,let's briefly understand-What is Z-shell ?

The Z-shell (Zsh) is a powerful and feature-rich shell for Unix-like operating systems. It offers several advanced features, including support for different editors, regular expressions (regex), and hooks.

It offers a variety of customization options that are not accessible in other shells. For command-line users who wish to customize their shell experience specifically to their needs, zsh has grown in popularity.

This tutorial will provide an overview of how to use editors, regex, and hooks with Z-shell. We will also address a few FAQs on how to use editors, regex, and hooks with Z-shell.

Prerequisites

Make sure your system has a terminal emulator installed and that your zsh –version is 5.8.1 or higher before beginning to work with the code in this tutorial.

You can update your zsh version if it is less than 5.8.1 and you are using Linux by using your system package manager. For instance, installing zsh 5.9 using apt on Ubuntu.

The homebrew program can be used by MacOS users to install an updated version of zsh:

brew update && brew install zsh

You need to install the moreutils package on your computer in order to utilize vidir and vipe. This package can be downloaded for Linux from joeyh.name/code/moreutils/ or installed using your Debian, Ubuntu, or Arch package manager. On MacOS, Moreutils is also accessible through homebrew or macports.

Finally, you are presupposed to be familiar with using command-line tools and editing files in the terminal in order to read this article. For some examples below., familiarity with zsh dotfiles, especially the .zshrc file:

Compatibility with Other Shells

zsh is one of many well-liked command-line shells, but its fundamental functionality is similar to that of other shells. For those occasions when you prefer certain choices provided by other shells, or you're testing out zsh for the first time, zsh also contains several compatibility options with other shells.

There are two types of tools offered by zsh to emulate the behavior of other shells:

  • Through shell options (setopt)
  • Through the emulate Builtin

Starting with setopt, let's look at what you can accomplish with these commands.

1. Through shell options (setopt)

For the sake of this post, we will only discuss the choices that are most useful for making your experience with zsh a little more pleasant when switching from another environment. The setopt application offers several possibilities for customizing your shell. The options listed below are all focused on giving the zsh shell varied behavior as needed.

But first, how do you even know which options in your shell are selected? A list of the options you have enabled will appear when you run setopt without any arguments. Even if this is useful, you might need to look a bit farther to find a complete list of the zsh shell's options. However, you can use ksh_option_print to view a complete list of all available options right in your shell.

ksh_option_print

You can switch the list of options printed by setopt from merely showing enabled options to show each option along with an associated on or off column by using the ksh_option_print option.

setopt kshoptionprint will also work
setopt ksh_option_print

Here's an abridged list of setopt options, each with an on/off indicator.

setopt
noaliases   off
allexport   on
noalwayslastprompt   off
alwaystoend   on
appendcreate   off
noappendhistory           off
autocd   on
autocontinue   off
noautolist   off
noautomenu   off
autonamedirs   off
noautoparamkeys   off
noautoparamslash   off
autopushd   on
noautoremoveslash   off
autoresume   off
nobgnice   off
braceccl   off
bsdecho   on

bsd_echo

The newline (\n) and tab (\t) characters that zsh prints by default are unnoticeable, but there may be circumstances in which you need the literal backslash escaped character to be present in a string. You can switch on bsd_echo during certain times.

setopt bsd_echo
# prints tab and newline characters
echo "\t\n"

unsetopt bsd_echo

# will print the literal "\t\n"
echo "\t\n"

csh_junkie_loops

An option called csh_junkie_loops provides a more succinct syntax for looping through lists or arrays. By turning on this option, you can utilize loops without having to use the do keyword before performing operations on each element of the loop. Additionally, you can substitute the done keyword for end using the csh_junkie_loops option, saving a keystroke.

Even though it may not initially seem like a significant gain, the condensed syntax can help you work with your shell more rapidly over time by removing the burden of having to recall the keywords required to construct a syntactically sound loop in zsh.

The example below:

the for command requires "do" before any actions
can be performed on the list of items
a traditional zsh loop
for f in 1 3 4 5; do print $f; done

You can use a more compact syntax when the csh_junkie_loops option is enabled to get the same effect:

setopt csh_junkie_loops
for f in 1 3 4 5; echo $f; end
$ 1
3
4
5

csh_nullcmd

You can use a command similar to >file.txt to empty the contents of a file in your terminal, which will replace the file's content with an empty string. The csh_nullcmd option can be enabled to stop file redirections from happening if you are working with sensitive data, or you don't want to overwrite files with empty data.

Any redirection that does not comply with a command will result in an error after this option has been activated. An example:

setopt csh_nullcmd

# attempting to overwrite file.txt will create an error
>file.txt
zsh: redirection with no command

Entering unsetopt chs_nullcmd in your terminal will disable this option.

ksh_arrays

The first item must be retrieved using $array[1] because arrays in zsh are One-indexed. For developers who are accustomed to using programming languages that support zero-indexed arrays, this behaviour seems odd.

You can use zero-indexing with arrays in ksh to emulate the behaviour of various programming languages. This option also demands that you access array items using curly braces rather than square brackets, which is the usual behaviour of zsh, where curly braces are optional.

declare -a kitchen_items=(plates cutlery oven sink)
print ${kitchen_items[1]}
# plates

When the ksh_arrays option is enabled, "cutlery" is referred to in place of "plates" in $kitchen_items[1].

setopt ksh_arrays
declare -a kitchen_items=(plates cutlery oven sink)

print ${kitchen_items[1]}
# cutlery

sh_word_split

Unlike the sh and ksh shells, zsh does not by default break multi-word variables into separate components. This implies that a variable containing three words will be considered as a single item rather than as three independent items if the words are separated by spaces.

kitchen_items="plates cutlery oven sink"

# without sh_word_split
for word in $kitchen_items; do
    print "$word"
end

Use setopt sh_word_split to make zsh split words on spaces in order to emulate sh's behaviour.

setopt sh_word_split

Let's use a slightly different syntax to print the items in $kitchen_items now:

kitchen_items="plates cutlery oven sink"
for word in $kitchen_items[@]; do
    print "$word"
end
# the loop above will print the following text:
# plates
# cutlery
# oven
# sink

The aforementioned setopt commands can be added to your .zshrc file for more permanent setopt usage, making them available in each interactive shell session. The aforementioned setopt examples can be used immediately in your shell.

However, let's have a look at the emulate command if you discover that you require an alternative method of emulating the actions of other shells.

2. Through the emulate Command

The emulate builtin command in zsh can be used to emulate another shell. For instance, you may use the command below to simulate the Bash shell:

emulate bash

This will make it possible for zsh to function mostly like the Bash shell. Afterward, you can use zsh to execute Bash scripts and commands, and they should function as intended.

You can substitute the name for bash in the emulate command to simulate other shells, such as the Bourne or Korn shells. For instance, you can use the following command to simulate the Korn shell:

emulate ksh

You can use any command you have if you want to run it in sh emulation mode.

emulate sh

Once configured, you can type emulate in your terminal to print the current emulation mode.

By using the -c option with the emulate command, "sticky" emulation can also be applied to functions. This indicates that once the emulate command has been executed, it will be in scope for any commands that are executed later in this function. This emulation will be running every time the function is called.

function hello_world_sh () {
    emulate sh -c  "print hello world";
}

When writing functions that must be POSIX compatible while still utilizing the zsh environment, the emulate sh -c command can be helpful.

Editing on the Fly

You might need to modify a variety of files as part of your everyday work, whether you're designing an app or simply crossing something off a to-do list. Working in the terminal has its advantages, and being able to swiftly change these files is one of them, especially when using a shell as flexible as zsh.

There are numerous tools for zsh that you may use to easily alter different elements of your environment from within zsh. I'll start with the zsh Line Editor, which is the interactive terminal's core.

zsh Line Editor

The editor you use with zsh to enter and change commands on the command line on your terminal is called the zsh Line Editor, or ZLE. The line editor in every shell is similar, so you usually won't need to change how it behaves.

ZLE exists as a zsh module called zsh/ZLE and activates immediately when zsh is loaded.

The built-in line editing interface in zsh is called ZLE (zsh Line Editor), and it enables you to edit the command line and navigate through the command history. You can interact with the command line using a variety of keybindings and functions provided by ZLE, including cursor movement, text deletion, and history access.

Since ZLE is the foundation of zsh's interactive features, it is always enabled if you are using it in an interactive shell. The command below can be used to confirm the zle option is set:

This command will print "ZLE: on" if the zsh Line Editor is enabled.
[[ -o zle ]] && echo "zle: on"

Setting your interactive shell environment's default keybindings is the primary method of directly communicating with ZLE. You can use these parameters to employ keybindings unique to the emacs or vi editor you prefer. The distinctions between these two editing modes are fully described in the zsh users guide.

In your terminal, type the following command if you'd rather use the emacs keybindings.

bindkey -e

Use the following command to make the vi keybindings active:

bindkey -v

Let's look at how you may utilize the zsh Line Editor with the zed editor now that you know a little bit more about it.

Zed

Zed uses ZLE to enable command-line script and function editing without launching an editor. This may be used as a simple editor for any generic editing command, and is excellent for rapid edits while you explore your file system. Zed exists as a short script, so it lacks some of the features of a full-featured editor, but its simplicity makes it an effective tool.

Start by typing zed into your terminal to confirm that it is already installed.

zed

In the absence of the zed command, you can download the source code from https://github.com/zsh-users/zsh/blob/master/Functions/Misc/zed and save it as an executable file or wrap it in a function that can be sourced.

When zed is ready, you can edit the command line by typing zed in your terminal to create a new one. The modified command line will be run by zsh when you save and close the editor.

You can use zed -f in the following manner to change a specific function in your zsh environment.

zed -f precmd

Keep in mind that the modifications you make to your function using zed won't take effect until you source .zshrc or a similar command to reload your configuration files.

Vared

Instead of manually setting a variable's value on the command line with the set or export commands, vared enables you to change a variable's value using your favourite text editor. You must first enter the name of the variable you wish to change before using the vared command. For instance, you can use the following to change the value of your EDITOR environment variable:

vared EDITOR

With ZLE now open, you can make any necessary changes to the value of the EDITOR environment variable. Use ctrl-c to save your changes and go back to the command line after you're done modifying your variable.

Editing Directory Names with Vidir

The names of directories (or files) can be changed using Vidir in your default editor. Instead of relying on pattern matching or loops, this is helpful for fast changing directory names visually. Use the command below, for instance, to change the names of every item in your $HOME directory.

vidir "$HOME"

A temporary file listing the names of each object in your $HOME directory will be created by Vidir. Vidir will modify the name of any file once you make your modifications and save it.

By providing your preferred filename to vidir as an argument rather than a directory, you can also use Vidir to modify the name of a single file. An illustration.

vidir /path/to/file.txt

Will open file.txt in your default editor, where you can change the filename as desired.

Using Editors in Pipelines with Vipe

Instead of using programs like awk or sed to manipulate the text inside a pipeline, it might occasionally be helpful to make changes to a pipe's contents directly in your usual editor. You can alter the output of the previous command graphically by inserting the vipe command into any pipe. This is extremely helpful when you can't apply a specific command because you don't know how the output of the command will appear.

The example that follows modifies the contents of the previous curl command using vipe and saves the modified version to a file called example_modified.txt.

curl "https://example.com" | vipe > example_modified.txt

Regular Expressions

If you are coming from a more simplistic shell, the vast collection of glob operators that zsh offers may be intimidating. Fortunately, zsh has regular expression support for pattern matching across your shell, which might be a better choice if you find globbing to be unclear or counterintuitive.

Using the noglob shell option is how I usually completely disable globbing in my environment, but it's not necessary:

setopt noglob

Regex in Test Blocks

Basic Regular expression matching is provided by zsh using the comparison operator =~. This enables you to execute if blocks while using regular expressions to match text. Let's check, for instance, whether $variable contains the words "txt" or "zsh":

variable="file.txt"

if [[ "$variable" =~ (txt|zsh) ]];
    then
        print "variable contains txt or zsh";
    else
        print "variable does not contain txt or zsh"
fi

Using zsh/Regex

You can use a flag to enable regular expressions in test scenarios with the zsh/regex module. To match text fragments, this flag applies POSIX extended regular expressions.

Using the zmodload command, you can load zsh modules into your environment:

zmodload zsh/regex

Once loaded, regular expressions can be used in test blocks by specifying the -regex-match flag. For instance, you might condense the if block from the previous example into a single-line test command. The matching section of the string is added to the environment variable $MATCH if a match is discovered.

zmodload zsh/regex

test "txt" -regex-match (txt|zsh) && echo "contains txt or zsh" || print "does not contain txt or zsh"

Additionally, printing the MATCH environment variable allows you to verify the precise match.

print $MATCH

txt

Using zsh/PCRE

The zmodload command can be used to load the zsh/prce module, same like zsh/regex:

note: pcre must be lowercase for the module to load
zmodload zsh/pcre

The three commands included with the zsh/pcre are pcre_compile, pcre_study, and pcre_match.

  • pcre_compile creates a regular expression compatible with Perl.
  • This command's options are the same as those for PCREs.
  • pcre_match a command that runs independently to compare strings to PCREs. Usage
pcre_compile -m "bark$"
string="dog says bark"
pcre_match -b -- $string
print $MATCH

Additionally, zsh/pcre provides the -pcre-match test condition, which is comparable to the -regex-match flag offered by the zsh/regex module previously stated and matches against strings.

test "txt" -regex-match (txt|zsh) && print "contains txt or zsh" || print "does not contain txt or zsh"

Respond to Events with Hooks

zsh Hooks are unique operations that the shell automatically performs at specified times, such as when a command is typed at the prompt or when the shell first launches. With the use of hooks, you are able to carry out certain operations or activities at these locations, such as changing the command before it is run or informing the user.

chpwd

When the current working directory is changed in zsh, a specific function called chpwd is immediately called. This enables you to carry out particular operations or tasks each time a user switches directories.

You must define the chpwd function in your .zshrc file before you can use it. Add the following lines to your configuration file to define the chpwd function:

chpwd () {
  # Your commands and actions go here
}

The chpwd function can be used to change the working directory displayed in the command prompt or to update the directory in the command prompt itself.

When the current working directory is changed after you've declared the chpwd function in your configuration file, it will automatically run. By modifying the directories in your zsh session and seeing the results of the chpwd function, you may test it.

periodic

The commands contained in a single function named periodic will be executed every n seconds, where n is the number of seconds provided in the environment variable $PERIOD. Say, for illustration, that you wish to activate a process that downloads my bookmarks once every six hours. You can use the following code to accomplish this.

The environment variable $PERIOD is used to establish the time and determine how frequently the function will execute.

periodic() {

}

precmd

When the terminal is repainted, this is not run. When a background process notifies your current prompt, the screen is redrawn.

My precmd function has a command that puts the current directory in a file as a more specific illustration. This enables me to launch a fresh terminal in the directory that was last used. This is very helpful if you need to restart your terminal after testing your environment or if it crashes accidently while you are working on a crucial project.

precmd () {
  pwd >"$HOME/.zsh_reload_directory.txt" &
}

This adds the current directory to a file in my $HOME directory called .zsh_reload.txt. Add the following command somewhere in your .zshrc file to have your terminal automatically browse to this directory when it launches.

create the precmd function
precmd () {
  pwd >|"$HOME/.zsh_reload_directory.txt" &
}

add the navigation command outside of the precmd function
cd "$HOME/.zsh_reload_directory.txt"

preexec

Executed following the reading of the command but before the command prompt is shown.

The precmd function is typically used to update the $PS1 command prompt variable with pertinent data, such as the time of your most recent command execution, but you are not restricted to doing so.

zshaddhistory

Every time a command is typed at the prompt, the unique function zshaddhistory is launched. This enables you to carry out particular functions or actions each time a command is entered, including logging the command or changing it before it is run.

You can specify zshaddhistory as a function in your .zshrc file to use it:

zshaddhistory () {
  # Your commands and actions go here
}

The zshaddhistory function can be used to alter the command prior to execution or to log it to a file.

As soon as a command is submitted at the prompt after you have defined the zshaddhistory function in your configuration file, it will be carried out immediately. Enter a command at the prompt to test it, then see how the zshaddhistory function behaves.

Adding New Hooks

You must first define a custom hook function containing the commands and actions you wish to carry out at the specified moment before using the add-zsh-hook function. You could, for instance, create a hook function that records the commands entered at the prompt as follows:

log_commands () {
  print "$(date): $1" >> ~/.zsh_command_log
}

Once your custom hook function has been defined, you can attach it to an already-existing zsh hook by using the add-zsh-hook function. For instance, you can use the following command to add the log_commands function to the zshaddhistory hook, which is used anytime a command is entered at the prompt:

add-zsh-hook zshaddhistory log_commands

As a result, each time a command is typed at the prompt, the log_commands function will be called, logging the command to the ~/.zsh_command_log file.

hooks-define-hook

To define a unique hook in zsh, use the hooks-define-hook function. When a command is entered at the shell prompt or when the shell first starts up, for example, zsh hooks are special routines that are automatically called at those points in the shell's operation. By defining a custom hook, you can define your own points in the execution of the shell at which to run custom commands or do custom actions.

You must first choose when and where you want your custom hook to be executed before using the hooks-define-hook function. The name and arguments of the hook will be decided by this. For instance, you might use the zshaddhistory hook, which is called with the command as its sole parameter, if you wanted your hook to be executed everytime a command is typed at the prompt.

After choosing the hook's name and arguments, you can define it by calling the hooks-define-hook function. For instance, you could use the following command to specify a hook that runs each time a command is typed at the prompt and logs the command to a file:

hooks-define-hook zshaddhistory log_command

This will define the command supplied at the prompt as the only argument for the hook, which is named zshaddhistory.

After defining the hook, you may attach a function to it using the add-zsh-hook function. The hook will call this function each time it is activated.

FAQs: Using Editors, Regex, and Hooks with Z-shell

What are some popular editors supported by Zsh? 

Zsh supports various editors, including Emacs, Vi/Vim, Nano, and Joe. You can configure your preferred editor using the EDITOR or VISUAL environment variables.

What are regular expressions (regex)? 

Regular expressions are patterns used to match and manipulate text based on specific rules. They provide a powerful way to search, extract, and modify text in tools like Zsh, grep, sed, and many others

How can I use regex in Zsh? 

Zsh supports regex through built-in features and pattern matching utilities. You can use regex for file globbing, filename matching, string manipulation, and more. Zsh provides operators and escape sequences to work with regex patterns.

Can I use hooks in Zsh? What are they used for?

Hooks in Zsh are functions that are executed in response to specific events or triggers. They are commonly used for customization and automation.

How do I define hooks in Zsh? 

To define a hook in Zsh, you need to declare a function and associate it with a specific event using the appropriate function name. For example, preexec() { ... } defines a pre-execution hook that runs before each command execution.

Can I use third-party plugins with Zsh? 

Yes, Zsh supports third-party plugins and frameworks like Oh My Zsh, Prezto, and Antibody. These plugins provide additional functionality, such as theme customization, auto-completion, and extended prompts.

Where can I find examples of Zsh hook usage? 

You can find examples of Zsh hook usage in the official Zsh documentation, online forums, and various Zsh community repositories.

Conclusion

In this tutorial, you learned how to use the Z shell to customize your workflow by using on-the-fly editors, using regex to match text and variables, and setting up hooks in your environment to automatically react to events in your shell.

This article only briefly touches on some zsh customization options; for more details, see the official documentation at https://zsh.sourceforge.io/.

If you have any queries or doubts, please leave them in the comment below. We'll be happy to address them.

Great! You’ve successfully signed up.
Welcome back! You've successfully signed in.
You've successfully subscribed to DevOps Tutorials - VegaStack.
Your link has expired.
Success! Check your email for magic link to sign-in.
Success! Your billing info has been updated.
Your billing was not updated.