sharkdp/fd
Command execution templates
Active contributors: tmccombs, sharkdp, Michael Aaron Murphy, Jonah Caplan
Purpose
-x/--exec runs a command per result; -X/--exec-batch runs a command once with all results as arguments. This page is the user-facing companion to systems/command-execution and systems/format-templates. Read those for implementation details.
The two modes
| Flag | Mode | Behaviour |
|---|---|---|
-x, --exec |
One-by-one | Spawns one child per result, in parallel up to --threads. |
-X, --exec-batch |
Batch | Spawns one child per batch of results. Multiple invocations occur if the command line would exceed ARG_MAX or --batch-size is hit. |
-l, --list-details |
Synthetic batch | Equivalent to -X ls -lhd .... fd picks GNU ls/gls automatically. |
-x, -X, and -l are mutually exclusive (declared via clap ArgGroup "execs" in src/cli.rs). They also conflict with --max-results, --quiet, and --max-one-result because exec mode is a "do something for every match" operation.
The placeholder language
Five placeholders, plus {{ / }} for literal braces:
{} — full path (e.g. dir/file.txt)
{/} — basename (file.txt)
{//} — parent directory (dir)
{.} — full path without ext. (dir/file)
{/.} — basename without ext. (file)
{{ — literal {
}} — literal }If you don't include any placeholder, fd appends an implicit {} to the end of the command. This is what makes fd -e zip -x unzip work without writing unzip {} explicitly.
In batch mode, each command may use exactly one placeholder. CommandSet::new_batch enforces this; using two distinct tokens is a parse-time error.
Argument boundaries
Everything after -x/-X is consumed as the command template until either end-of-args or a literal \;. The clap declarations use value_terminator(";") and allow_hyphen_values(true) to make this work.
That means:
# This treats `pattern path` as fd args because of the literal `\;`:
fd -x echo \; pattern path
# Whereas this passes `pattern path` to echo:
fd -x echo pattern path
# In practice the recommended form is to put -x last:
fd pattern path -x echoMultiple -x/-X invocations
-x (and -X) can appear multiple times. Each occurrence is parsed into a separate CommandTemplate, and all of them run for each result (or each batch). Useful for fan-out:
# Convert each .jpg to .png AND log a copy of the path:
fd -e jpg -x convert {} {.}.png -x echo converted: {}Parallelism
-x runs up to --threads N commands in parallel (default min(available_parallelism, 64)). Use --threads=1 for serial execution; this also disables stdout/stderr buffering so a child can interact with the user's terminal (e.g., fd ... -x vim).
-X is single-process by definition. Each batch's child runs synchronously inside CommandBuilder::finish.
Output ordering and --print0
- With
-xand multiple threads, fd captures each child's stdout/stderr into anOutputBuffer(src/exec/command.rs) and flushes it under a stdout/stderr lock when the result completes. This guarantees output blocks for each result are atomic. - With
-xon a single thread, fd inherits stdio so children can be interactive. -0/--print0makes the per-result output blocks\0-separated. As of 10.4.0 this also affects the boundary between separate-x/-Xinvocations within a single result. SeeCHANGELOG.md10.4.0 entry.
Exit codes
The exit code of the entire fd run with exec is the OR-merge (merge_exitcodes in src/exit_codes.rs) of:
- The walker's exit code (typically
Success). - The exit codes of every spawned child.
Any non-zero child status produces ExitCode::GeneralError (1). This is intentional: scripts can rely on fd ... -X cmd exiting non-zero if any subcommand failed.
Common recipes
# Unzip every .zip in parallel:
fd -e zip -x unzip
# Format every C++ file in place:
fd -e cpp -e h -e hpp -x clang-format -i
# Open every test file in vim (single instance):
fd -g 'test_*.py' -X vim
# Show details (long listing) for everything:
fd -l # equivalent to: fd -X ls -lhd --color=auto
# Combine with ripgrep:
fd -e cpp -e h -X rg 'std::cout'
# md5sum every file, parallelised:
fd -tf -x md5sum
# Convert .jpg -> .png keeping the directory structure:
fd -e jpg -x convert {} {.}.png
# Delete every .DS_Store under the cwd:
fd -H '^\.DS_Store$' -tf -X rmCaveats
- Aliases and shell functions cannot be invoked directly.
fd ... -x my_aliasruns the literal programmy_alias. To use a shell function you needfd ... -x bash -c 'my_func "$1"' bash. - Placeholders may need quoting to keep your shell from interpreting them.
'{}'and'{/.}'are safe in any shell. -x rm -rand race conditions: when removing nested matching directories, the parent may be deleted before the recursivermreaches the child. The README calls this out as harmless, but be aware.- Order is not guaranteed. The walker is parallel and the batches are not sorted. Sort the input with another tool if you need a deterministic order.
Entry points for modification
- Add a new placeholder. See systems/format-templates — that's where the engine lives.
- Change parallelism for
-x. EditWorkerState::receiveinsrc/walk.rs. The number of exec workers comes fromconfig.threads. - Change the synthesised
-lcommand. Editdetermine_ls_commandinsrc/main.rs.
Related pages
- Command execution — the implementation.
- Format templates — placeholder parsing/expansion.
- CLI parsing — how
Opts::execis populated.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.