November 23, 2017·4 min read·engineeringfishscriptingargumentshistory

fish 2.7 gives small functions consistent command interfaces

fish 2.7 ships argparse, directory-history selection and new string operations. These additions reduce handwritten parsing while keeping option syntax and text-encoding contexts explicit.

A shell function often begins as a command with a few fixed arguments and gradually acquires an interface. Adding optional values and repeatable flags can make the parser larger than the operation it controls. fish 2.7 supplies argparse so functions can share option behavior with builtin commands instead of maintaining their own argument scanner.

The November 23 release notes carry the October beta's parsing and directory-history work into the stable release. They also state that Xcode builds and macOS packages, which could not be produced for the beta, work again. Users upgrading from 2.6 receive the beta's substantive features as part of this stable version.

I value fish for reducing the configuration needed for a useful interactive shell. The same preference applies to functions written for that shell. A helper should not need an elaborate parser simply to behave like the commands around it.

A parser should describe the command's contract

The 2.7 argparse documentation contrasts the new builtin with external getopt and manual scans through argv. GNU and BSD versions of the external command differ, while hand-written scans need considerable repeated code to match builtin behavior.

The builtin accepts option specifications before a required --, followed by the arguments being parsed. It sets local _flag_ variables for recognized flags and leaves remaining operands in local argv. This separates the command's option contract from the body that acts on its inputs.

Specifications distinguish required values, optional values and repeated values. A suffix of = requires a value and retains the last occurrence. A suffix of =+ retains values from repeated occurrences. These are different contracts, and the syntax makes their difference visible.

function show_labels
    argparse --name=show_labels 'l/label=+' -- $argv
    or return

    for label in $_flag_label
        printf '%s\n' "$label"
    end
end

The function accepts repeated --label or -l options. Its loop consumes the values collected by the parser. Invalid arguments stop the function through or return, so the body does not proceed on a failed parse.

A parser specification should not be confused with the helper's complete purpose. The example prints labels and leaves other operands unused. A real command can apply minimum or maximum operand counts when its operation requires them, using the parser options documented by the release.

Validation distinguishes syntax from meaning

A supplied option value can be syntactically present yet unsuitable for the command. argparse allows validation code after an exclamation mark in an option specification. The release includes an integer-validation helper with optional minimum and maximum bounds.

The validation code receives information about the command, the flag and its value. It returns zero for an acceptable value and nonzero for rejection. The documentation specifies that validation diagnostics go to standard output, even though the parser's own error reporting uses standard error.

That detail matters for authors extending the parser. A validation function needs to follow the callback's conventions, rather than assuming all diagnostic paths have identical stream behavior. Functions used for validation also need --no-scope-shadowing to access the supplied variables.

The builtin can additionally express mutually exclusive flags. These rules let a reader inspect the accepted combinations before examining the command's operational code. Parsing, semantic validation and execution remain separate stages even when the builtin helps organize them.

I prefer that division because an argument error should remain attached to the argument contract. A function's main operation becomes easier to follow when it receives already checked inputs.

Directory history gets a direct selection command

The cdh reference documents a menu of recently visited directories when invoked without arguments. A user can select by letter or number, or use the completion pager. With one directory argument, it behaves like cd.

The candidates come from dirprev and dirnext. The reference says cd retains the 25 most recently visited directories. This makes cdh a chooser for navigation history, not a general search engine for paths that have never been visited.

The documentation also explains that making those history variables universal shares directory history across fish instances. That is an explicit choice about the scope of navigation state. A directory chooser using shared history can present places visited in another shell session.

There is no need to maintain an independent bookmark file for this particular operation. The command uses state that directory navigation already records. That narrow responsibility keeps its behavior understandable.

String operations identify their encoding context

The release adds string lower and string upper, while warning that case conversion is not yet locale-aware. A lowercase operation is therefore not a promise to implement every language's case rules. The qualification is part of the feature's contract.

The string reference also documents escaping styles for scripts, variable names and URLs. These contexts have different validity rules. A value encoded for a variable name is not automatically an appropriate shell expression or URL representation.

The variable-name style hex-encodes non-alphanumeric characters after converting the string to UTF-8. URL style encodes characters not legal in a URL. Script style is the default and produces a representation intended to reconstruct the original argument through shell evaluation.

String operations also have command statuses that describe whether they changed or matched input. The reference says string lower returns zero when it converts at least one string, and one otherwise. With quiet mode, that behavior lets a function test whether text is already lowercase without printing a transformed value. A nonzero status in that case is a documented result of the operation, not evidence that the builtin could not run.

The corresponding string unescape operation reverses the selected style for properly formatted input. Invalidly formatted values are ignored, according to the reference. A caller therefore needs to know the encoding context before choosing how to decode a string.

These additions make common function operations available through fish's own commands. Argument specifications describe how callers supply values, while string styles describe how values enter another syntactic context. The distinction prevents a general claim about escaping from hiding the actual rules a particular consumer needs.