Fish Abbreviations: Expansion Timing, Context Rules, Regex, and Persistence
Use Fish abbreviations as visible command-line transformations with precise context, regex and function rules, cursor placement, persistence, and tests.
Fish abbreviations transform typed text in the interactive command line. After a matching token is entered and the user presses Space or Enter, Fish replaces that token with the configured expansion. The user can inspect and edit the expanded command before it executes. This makes an abbreviation closer to an interactive text transformation than a shell alias or script-level macro.
That distinction determines where abbreviations belong. They improve interactive typing, but they are not expanded in scripts. They also do not validate what the eventual command will do. Keep executable command logic in functions or programs, and use abbreviations only to make a visible command line shorter or easier to compose.
Expansion happens in the editor, not in a script
A simple abbreviation can expand a short command token:
abbr --add gco --position command 'git checkout'
When a user types gco in command position and accepts it with Space or Enter, Fish inserts git checkout into the editable command line. The command is not run merely because the abbreviation expanded. The user can add a branch, inspect the arguments, or cancel.
An abbreviation is not a replacement mechanism for noninteractive scripts. Fish explicitly documents that only typed-in commands use abbreviations. A script that needs reusable behavior should call a function or executable directly. This avoids a configuration-dependent difference where a command works in one user’s prompt but fails under automation.
The default position is command, which is often the right choice for command shortcuts. The anywhere position allows the word to expand elsewhere in the line, which can be convenient for flags or snippets but can also rewrite an argument value unexpectedly. Choose the narrowest position that matches the intended context.
Scope abbreviations by command when needed
The –command option constrains an abbreviation to an argument of a named command. It implies anywhere positioning and cannot be combined with command-position mode. Command scoping helps disambiguate common short words that should mean different things in different CLI contexts.
# Expand co only when it is an argument to git.
abbr --add --command git co checkout
# Expand a local shorthand anywhere in the command line.
abbr --add --position anywhere -- -C --color
The second form includes a separator because -C begins with a dash and would otherwise be interpreted as an option to abbr. Command-scoped abbreviations still require unique names. When one token needs different expansions for different commands, use a carefully designed regular-expression abbreviation or distinct names instead of assuming duplicate names are automatically disambiguated.
Context rules should be tested with the full command line, not only the abbreviation token. Confirm whether a token in command position, an option value, a path, and a quoted argument is eligible for expansion. A convenience that unexpectedly transforms a value can be more disruptive than the typing it saves.
Use regular expressions as exact-token matchers
With –regex, Fish interprets the pattern using PCRE2 and matches the entire token. If more than one abbreviation matches the same token, the one added last is used. These semantics make order part of the configuration: a broad pattern added later can override a narrower rule.
function open_text_file
set -l escaped (string escape --style=script -- $argv[1])
printf 'editor %s\n' $escaped
end
abbr --add text_file --position command \
--regex '.+[.]txt' --function open_text_file
The helper uses string escape so a filename containing spaces or shell punctuation remains one argument when the returned command text is inserted into the editor. A production helper should emit the actual intended command text. Test filenames containing spaces and punctuation, and use Fish list handling rather than converting the argument into a whitespace-delimited string.
Prefer a literal abbreviation when a literal token expresses the intent. Regex rules are useful for families of tokens, but they make it harder to predict which text will be rewritten. Document broad patterns and their ordering, especially when multiple configuration files contribute abbreviations.
Dynamic expansions are functions with a success contract
An abbreviation can call a Fish function to compute its replacement. Fish passes the matched token to that function. If the function exits with status zero, its output replaces the token; if it exits nonzero, the token is left unchanged. No fixed expansion can be specified at the same time as the function option.
function select_context_abbreviation
switch $argv[1]
case prod
printf '%s\n' 'deployctl --context production'
case stage
printf '%s\n' 'deployctl --context staging'
case '*'
return 1
end
end
abbr --add context_alias --position command \
--regex '^(prod|stage)$' \
--function select_context_abbreviation
The function should be deterministic and quick because it runs during interactive editing. Avoid network requests, mutations, and slow discovery steps in dynamic expansions. If the function cannot produce a confident replacement, return nonzero and allow the original text to remain. This is safer than turning a typo into an unintended command.
Treat the function’s output as visible command-line text. Keep it auditable, avoid injecting values from untrusted sources, and do not make expansion itself perform the operation. A function that prints a command and a function that executes a command have very different consequences even if both are described informally as “expanding” an abbreviation.
Cursor markers turn abbreviations into templates
For a fixed multi-part template, –set-cursor places the cursor at a marker in the expansion and removes that marker from the inserted text. If no marker is specified, Fish uses percent as the default marker. The first marker occurrence is the cursor target.
abbr --add review-log \
--set-cursor 'git log --oneline --decorate -- %'
The user can type a path or pathspec where the cursor lands, inspect the result, and then run it. This is useful for commands that are safe to prepare but need a value before execution. Make sure the marker appears in the intended position and does not survive as literal command text.
For a longer multiline template, use a marker near the first place the user should edit. Keep templates small enough to inspect at the prompt. If a template contains control flow or many repeated values, a function or a dedicated script may be more maintainable than an abbreviation.
Persist definitions in configuration
Since Fish 3.6, abbreviations are no longer saved in universal variables. Fish can import existing values for compatibility, but the recommended configuration is to add abbr declarations in config.fish or in a separate file under conf.d. If abbreviations are currently imported from old universal variables, inspect their exported form, move the intended declarations into a configuration file, and then erase the old variables to avoid duplicates.
# ~/.config/fish/conf.d/abbreviations.fish
abbr --add gco --position command 'git checkout'
abbr --add gst --position command 'git status --short'
abbr --add --command git co checkout
Use abbr –show to display definitions in an import/export-friendly form. Treat its output as configuration data: review it before appending to a file, especially if the file already exists. An automated redirect can overwrite a whole file and erase unrelated custom configuration.
Keep dynamic expansion functions in the normal Fish function path or define them in configuration before the abbreviation that references them. Persisting the abbreviation does not automatically persist a transient function definition that existed only in the current shell.
Inspect, query, rename, and remove definitions
The abbr builtin can list names, show importable definitions, test whether a name is configured, rename an abbreviation, and erase definitions. Use these operations to diagnose why a token expands or to remove one rule without rebuilding all abbreviations.
abbr --show
abbr --query gco
abbr --rename gco gco_branch
abbr --erase gco_branch
For command-specific entries, provide the relevant –command context when erasing or renaming so Fish can identify the intended definition. Review the output of –show after editing startup files to confirm that duplicate or legacy entries are gone.
When an abbreviation does not expand, check whether the current shell is interactive, whether the input is typed rather than executed from a script, whether the token is in the configured position, and whether a later abbreviation with the same matching scope wins. If an expansion is surprising, inspect regex rules and their registration order.
Build an acceptance test for your abbreviation set
For each definition, test the exact abbreviation at the prompt and verify its expansion after Space and Enter. Check command position versus argument position, command-specific scope, a non-matching token, and any cursor marker. For dynamic functions, test both the success path and the nonzero path. Verify that scripts do not depend on the interactive transformation.
Review the expanded command before running it. An abbreviation is a convenience layer, not a safety check or a permission boundary. If the transformation can cause a costly or irreversible action, make the inserted text especially clear and require the user to press Enter after inspecting it.
Maintain abbreviations like source code: keep one canonical configuration file, review changes, and test after upgrading Fish or changing shell startup paths. The user-visible command line should remain predictable even if the abbreviation file is absent; any behavior required by automation belongs in a function or executable interface.
Related:
Sources: