OFFSET偏移
← All articlesOFFSET / FIELD NOTESNO. 010

From selecting files to handing the directory back to the shell.

Distinguish finding, filtering, selection, and the file buffer to complete batch operations and pass the final directory back on exit.

English translation · Code and commands follow the Chinese original.

IN THESE READING PATHSA terminal file workbench ↗
About 8 min readStart reading

Yazi's everyday value is in joining navigation, selection, and file operations into one continuous workflow. First establish your current directory and the objects you intend to act on, then perform the action, and finally pass the result back to the shell. The following uses the default keybindings in 26.9.1; personal plugins may override them, so check the help panel before use.

Navigate to the directory, then find the file#

Pass the startup directory directly as a positional argument; multiple paths create multiple tabs. Passing an existing file opens its parent directory and focuses that file. Paths containing spaces must still follow the quoting rules of the shell you launch Yazi from.

yazi
yazi ~/Downloads
yazi ~/Downloads ~/Pictures
yazi ./examples/report.md

The current version neither needs nor provides the --cwd or -i options found in older notes. In the file list, j/k move down/up, h returns to the parent directory, and l enters a subdirectory; H/L go backward/forward in browsing history. Press g, g in sequence to go to the top, G to go to the bottom, and Ctrl-u/Ctrl-d to move half a page. J/K scroll the preview; they are not half-page navigation keys for the list. [1]

When working across directories, press t, t in sequence to open a new tab in the current directory, 1–9 to switch to a specific tab, and [ or ] to switch to an adjacent tab. A single t is no longer the complete new-tab binding. Open ordinary files with o or Enter, and choose an opener with O. Opening files directly with l requires extra customization such as smart-enter and should not be presented as default behavior. [2]

Distinguish find, filter, and recursive search#

Default keyScope and effect
/、?Find names forward or backward in the current list; n/N jump between matches
fFilter the current list to temporarily show only matching entries
sSearch filenames recursively with fd
SSearch file contents recursively with ripgrep
z、ZInvoke fzf navigation and zoxide directory jumping, respectively; each requires its corresponding dependency

If a directory contains a hundred files and you just want to find a name, use / without starting a recursive search. To keep only .jpg entries in view, use f. Consider s or S when you do not know how deeply the file is nested. Filtering only changes the list you see; it does not move files. Searching produces a results view, so seeing “only a few results left” does not mean files have been removed from the original directory either.

Use the period key to show hidden files, and comma-prefixed sequences to sort, such as , m for modification time or , n for natural ordering; ~ opens help. When leaving a filter or search, Esc cancels states one level at a time according to the current state. To explicitly clear a particular state, you can bind escape --filter, escape --search, or escape --select to your own keys. [3]

Hovered item, selection, and yank buffer are three distinct states#

The hovered item is the file under the cursor, the selection is the set of items you have explicitly marked, and the yank buffer holds sources waiting to be copied or moved. By default, Space toggles selection of the current item and then moves down one entry, making repeated presses useful for choosing nonadjacent files. v enters range-selection mode; V enters range-deselection mode. V does not mean “undo all selections on exit.”

Many actions prioritize the selection, so moving the cursor to a different file does not necessarily change what the action targets. Explicit options such as open --hovered and rename --hovered force an action to operate only on the hovered item. Depending on the current state, Esc can leave visual mode or clear the selection, but do not expect one press to clear every state. Selections retained across directories particularly need checking before a batch operation.

Key or actionResult
y / yankFile sources in copy mode, waiting for p
x / yank --cutFile sources in move mode, waiting for p
c c / copy pathCopy path text to the clipboard
Y or X / unyankClear the file sources waiting to be pasted

Pasting a path into an editor and copying a file to another directory use two different data channels. Path text on the system clipboard is not a file object. A working clipboard tool does not mean Yazi's file-paste operation can receive files from other applications.

Copying, moving, and bulk renaming#

To organize a downloads directory, for example, select two hypothetical reports with Space, press y, enter the destination directory, and press p. To move them, replace y with x. By default, p pastes into the current directory; it does not automatically enter a subdirectory just because the cursor is hovering over it. That behavior comes from plugins such as smart-paste. Once a copy or move starts, use w to inspect tasks and failures; a brief flash in the list does not prove that everything finished.

# 操作顺序,不是 Shell 命令
源目录:选择 report-a.pdf 和 report-b.pdf → y
目标目录:进入 archive/ → p
移动时:选择来源 → x → 进入目标目录 → p
取消待粘贴来源:Y

P pastes with forced overwriting, so confirm its scope first. d moves files to the trash, while D permanently deletes them; both have corresponding confirmation flows by default. Trash availability depends on the platform and filesystem, so recovery cannot be guaranteed unconditionally for every path. Do not add an rm -rf binding that bypasses Yazi's confirmation just to “delete faster.”

Press a to create a file; to create a directory, end its name with a slash, such as archive/. Press r to rename a single file; after selecting multiple files, r opens bulk renaming in a text editor. The following shows how to edit the filename list: preserve the number and order of lines, change only the destination name on each line, then save and exit, check the mapping, and review the confirmation prompt.

# 编辑前:每行一个名字
photo-01.JPG
photo-02.JPG
photo-03.JPG

# 编辑后:保留对应顺序
photo-01.jpg
photo-02.jpg
photo-03.jpg

Do not treat :rename img-{i}.jpg as a built-in bulk-renaming template. The current implementation looks for a blocking editor through the text-file opening rules and displays a before-and-after mapping. Duplicate destination names, changed files, and other conditions may cause some entries to fail. A batch operation is not a transaction that can be rolled back as a whole: read the failure report and do not blindly repeat the operation on entries that already succeeded. [4]

Shell input is not a Vim command line#

By default, semicolon opens nonblocking shell input and colon opens blocking shell input. You enter external shell commands, not Vim-style :cd, :touch, or :!.... Even running cd here changes the child shell's directory; it does not replace Yazi's cd action. To enter a directory path, use g, Space or bind cd --interactive yourself.

In the current format parameters, %h is the hovered path and %s represents all selected paths; use %% for a literal percent sign in the built-in format. Do not mix the old $0/$@ file-argument syntax into these templates. The read-only information action below goes in keymap.toml and invokes the system's file command to inspect the selected items. Keeping the raw command template after -- avoids another nested layer of action-argument quoting.

[[mgr.prepend_keymap]]
on = ["g", "i"]
run = "shell --block -- file -- %s"
desc = "Inspect selected file types"

A template's ability to pass paths does not mean every tool accepts multiple inputs. Before writing an external command, check the tool's arguments, the number of paths, and the objects it will modify; use blocking mode when interaction is required or you need to see the output. These remain documentation examples and have not been executed on real files. [5]

Pass the last browsed directory back to the parent shell#

A child process cannot directly change its parent shell's working directory. Automatically changing directory on exit relies on a function: Yazi writes the last directory to a temporary file, and after it returns, the current shell reads that file and runs its own cd. The following is the current official Bash/Zsh wrapper. You can choose a different function name; it does not work in every POSIX shell.

function y() {
    local tmp cwd; tmp="$(mktemp -t "yazi-cwd.XXXXXX")"
    command yazi "$@" --cwd-file="$tmp"
    IFS= read -r -d '' cwd < "$tmp"
    [ "$cwd" != "$PWD" ] && [ -d "$cwd" ] && builtin cd -- "$cwd" || builtin true
    command rm -f -- "$tmp"
}

Launch with y and exit with q to receive the directory; the default action for Q is quit --no-cwd-file, which does not output a directory, leaving you in the original location. IFS= and read -r preserve whitespace and backslashes. Reading with an empty delimiter avoids stripping trailing newlines from directory names as simple command substitution does. Checking that the directory exists also prevents cd from running on an empty result. The alias alias y=yazi has none of this post-exit reading logic. [6]

This handoff affects only the shell that invoked the function; another terminal or tmux pane will not change directories with it. NixOS/Home Manager users should arrange the appropriate shell integration in their declarative configuration rather than manually editing managed rc files. Check your own configuration and current shell to see whether a wrapper already exists; the function in this article does not establish the existing state of anyone's system.

Pass file selections to other programs#

--cwd-file outputs a directory, while --chooser-file outputs selected files; they are not interchangeable interfaces. In chooser mode, triggering an open action writes the selection and exits; q can be used to cancel. The caller should handle an empty result first, then read entries individually rather than treating multiple lines of paths as one file. Here is a read-only Bash example for receiving the result:

#!/usr/bin/env bash
selected="$(mktemp -t yazi-selection.XXXXXX)" || exit 1
trap 'rm -f -- "$selected"' EXIT
command yazi --chooser-file="$selected"
while IFS= read -r path || [ -n "$path" ]; do
    [ -n "$path" ] || continue
    printf 'Selected: %s\n' "$path"
done < "$selected"

This file format separates paths with newlines. If a filename itself contains a newline, this general-purpose script cannot split the result unambiguously, so the caller should define the input boundaries explicitly. When passing the result to an editor or desktop application, also check whether multiple files are accepted and whether cancellation should simply end the process. A reliable terminal workspace ultimately depends on making the objects and boundaries clear at every handoff.

References & further reading

  1. Yazi 26.9.1: Startup arguments ↗
  2. Yazi 26.9.1: Default keybindings ↗
  3. Yazi: Escape and cancellation states ↗
  4. Yazi 26.9.1: Bulk renaming ↗
  5. Yazi: Shell command templates ↗
  6. Yazi: Shell wrapper ↗

Adapted from technical notes. Examples use generic names.

Continue along a reading path

A terminal file workbench

  1. 01Separate Yazi's configuration, then wire up file openers.
  2. 02From selecting files to handing the directory back to the shell.
CONTINUE READING / NEXT ARTICLERootless containers: whose file permissions count?

Trace UID mappings through bind mounts, persistent data, and network boundaries to explain Podman's permission checks.