How to Use Editors, Regex, and Hooks with Z-shell
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.