fish 3.4 adds quoted command substitutions
Dollar-parenthesis substitutions let fish preserve multiline command output inside double quotes, while terminal theme selection reduces configuration overhead.
A command's output can be one text value or several arguments, and shell syntax needs to make that choice visible. fish 3.4.0, released on March 12, adds dollar-parenthesis command substitutions. The release's own changelog says the new form retains fish's existing substitution behavior when unquoted and can also appear inside double quotes. The quoted form prevents line splitting of the captured result.
This is more consequential than adding familiar punctuation. fish already has parenthesized command substitutions, but preserving multiline output previously required a collection step such as string collect. The new syntax makes that intent available where a caller is already specifying an argument through quotation.
I prefer an interactive shell that makes ordinary operations convenient without demanding a large configuration framework. This change fits that preference because it reduces syntax around a specific operation while preserving a visible distinction between a text argument and a list of lines. It does not require pretending that every shell interprets the same symbols identically.
Command output becomes arguments
The 3.4.0 language reference describes command substitution as executing an inner command and supplying its output as arguments to an outer command. With fish's ordinary unquoted substitution, each output line becomes a separate argument. An entire pipeline can be the inner command.
For a file containing several lines, these assignments consequently have different meanings. The first captures line-separated values, while the second captures the output without splitting it into a list at the line boundaries.
set lines (cat data.txt)
set text "$(cat data.txt)"
The existing parenthesized spelling remains valid. The release notes explicitly say unquoted $(command) has the same meaning as unquoted (command). Someone adopting the new spelling therefore should not infer that it changes the usual unquoted splitting rule.
That rule is line splitting. The release notes emphasize that the unquoted dollar form still splits on newlines only. Familiar syntax from another shell is useful for recognition, but the authoritative behavior remains fish's own language definition. Reading a command substitution requires attending to both the shell and the quotation around it.
Quoted capture is useful when the receiving command expects one argument containing text. Without it, a multiline result can change the number of arguments supplied. That difference is observable even when the characters printed by a simple test command look similar. An argument list and one string with embedded newlines are different interfaces.
Empty output needs a deliberate representation
The release also adds string collect --allow-empty. Its changelog entry explains that it produces one empty argument when a command substitution has no output. The distinction matters for commands that require an argument even when the intended text is empty.
For example, the notes connect this behavior with checking an empty captured string. The quoted dollar substitution expresses the same intent more directly in that case.
test -n "$(printf '')"
The test asks whether the captured text is nonempty. It does not accidentally omit the text argument merely because the producing command printed nothing. That is a small but useful improvement in the relationship between output capture and argument construction.
The language reference also documents explicit splitting through string split or string split0 at the end of a substitution pipeline. Those splits determine the produced arguments. Quoted capture is therefore one choice among several intentional representations, including line-based arguments and explicitly separated data.
The same reference places a default limit of 100 MiB on data read by command substitution. Reaching it prevents the outer command from running and sets a specific failure status. Command substitution is a way to construct arguments, so unlimited capture would not be appropriate merely because the producing command can emit a large stream.
Capture is also different from process substitution. The language reference documents fish's psub command for a consumer that requires filenames instead of text arguments. It stores output in a temporary file and supplies that filename to the outer command. Quoting a dollar substitution does not provide the same interface. A caller should choose according to whether the receiving program accepts text, standard input, or a path to a file.
Variable scope gets a more direct name
fish 3.4 adds --function to set and read. The release notes distinguish it from --local, whose scope is the current block. A variable created within an if block using local scope disappears when that block ends, even if the enclosing function continues.
The function-scoped option lets a value remain available for the rest of the function. The following example illustrates the difference without relying on an external program.
function choose_label
if true
set --function label example
end
printf '%s\n' $label
end
The versioned language documentation gives function and block scopes separate definitions. Function variables end with the executing function. Local variables end with the block in which they were created. The explicit option makes a common intention easier to state when control flow introduces an additional scope.
I find that distinction useful because a short shell function can otherwise conceal a lifetime decision in an innocuous-looking assignment. Naming the intended scope makes the code easier to interpret. It also helps explain why a variable can be available in one part of a function and absent immediately after a conditional.
Configuration can stay in the terminal
The 3.4.0 fish_config reference documents a new terminal interface for themes. A browser-based configuration interface still exists, but selecting a color scheme no longer requires opening it. The release notes give Nord as one included example.
fish_config theme choose Nord
The command loads the theme into the current session. The reference separately documents listing themes, displaying previews, and dumping the current theme in a loadable format. Theme files can come from fish's distributed themes or the themes directory inside the user's fish configuration directory.
Those operations separate trying a configuration from preserving it. A user can view sample text and choose a theme during a session before deciding how to save the settings. The terminal interface is especially useful where opening a local browser would interrupt the task that prompted the adjustment.
The release also makes autosuggestions optional through fish_autosuggestion_enabled. Setting it to zero disables them. The new None theme disables almost all highlighting, while retaining necessary visual distinctions such as the difference between an autosuggestion and the actual entered command line.
That is consistent with treating interactive assistance as configurable behavior. Autosuggestions and highlighting can make a shell easier to use, but they are separate from the meaning of the command being edited. A setting that disables assistance should not require replacing the shell's scripting language or rebuilding a prompt configuration.
Terminal width is different from string length
Another 3.4 improvement concerns visible width. string pad now excludes recognized escape sequences, and string length --visible measures the terminal cells occupied by text containing such sequences. The changelog illustrates this with colored text.
This matters for prompt alignment because a color escape contributes bytes to a string without occupying the same number of visible cells. Counting raw characters can therefore produce padding that is wrong on screen. The new option makes the intended measurement explicit for text that fish knows how to interpret.
These display improvements have a different purpose from the substitution change. One controls how command output becomes arguments. The other controls how text occupies a terminal. Both benefit from naming the representation the caller actually wants instead of requiring a workaround around a misleading default measurement.
Globs retain their own rules
The release notes also report faster glob expansion, particularly on glibc systems and in large directories with numbered filenames. The documented example can approach half the previous expansion time in some cases. That is a qualified observation about a particular workload, not a universal reduction in shell execution time.
The versioned language reference explains that fish sorts wildcard matches case-insensitively and sorts names containing numbers naturally. It also describes how unmatched globs normally prevent command execution, with specific exceptions such as variable assignment and loops. Performance work does not remove those language rules.
The practical effect of 3.4 is that several frequent operations need less supporting syntax or configuration. A function can state its variable scope directly, a theme can be selected in the terminal, and captured output can remain one argument through quotation. In the last case, the surrounding quotes now communicate the intended argument boundary where the command appears.