0

Before switching to fish shell, I frequently used various commands in zsh with which some_command. An example might be:

$ file `which zsh`
/opt/local/bin/zsh: Mach-O 64-bit executable arm64
/bin/zsh:           Mach-O universal binary with 2 architectures: [x86_64:Mach-O 64-bit executable x86_64
- Mach-O 64-bit executable x86_64] [arm64e:Mach-O 64-bit executable arm64e
- Mach-O 64-bit executable arm64e]
/bin/zsh (for architecture x86_64): Mach-O 64-bit executable x86_64
/bin/zsh (for architecture arm64e): Mach-O 64-bit executable arm64e

When I try to do this with fish it fails:

$ which zsh
/opt/local/bin/zsh

$ file `which zsh`
`which: cannot open ``which' (No such file or directory)
zsh`:   cannot open `zsh`' (No such file or directory)

Any idea of why this doesn't work fish as opposed to other more bash-like shells?

muru
  • 69,900
  • 13
  • 192
  • 292
ylluminate
  • 591
  • 7
  • 16
  • 2
    In general, even in Bash, Zsh, POSIX sh, etc., the use of backticks is strongly discouraged. See the [POSIX standard](https://pubs.opengroup.org/onlinepubs/9699919799/xrat/V4_xcu_chap02.html#tag_23_02_06_03), *"Because of these inconsistent behaviors, the backquoted variety of command substitution is not recommended for new applications that nest command substitutions or attempt to embed complex scripts.*" Reference [this U&L question](https://unix.stackexchange.com/q/126927/432493). – NotTheDr01ds May 31 '22 at 18:51

2 Answers2

6

fish does not use backticks for command substitutions. Instead one can use parens: file (which zsh) or (in release 3.4.0 and later) file $(which zsh). These mean the same thing.

Check out fish for bash users for other differences.

NotTheDr01ds
  • 3,321
  • 1
  • 5
  • 28
ridiculous_fish
  • 1,661
  • 10
  • 15
  • Too bad it's not a little more terse/abbreviated regardless of the POSIX standard. Wonder if there's a way to hack in an abbreviation of sorts? – ylluminate May 31 '22 at 19:00
  • 1
    @ylluminate I'm not sure how you can get any more terse/abbreviated than `(command)`. It's the same number of characters as using backticks, just (much) less error-prone when nesting. – NotTheDr01ds May 31 '22 at 21:46
1

FWIW, in zsh, you'd rather use:

file -- =zsh

Or:

() {file -- $1:c} zsh

file $(which zsh) (the modern version of the deprecated file `which zsh`) would only work if there was no alias or function also defined for zsh and if the path to the zsh command didn't start with - nor end in newline characters¹ and didn't contain characters of $IFS (space, tab, newline and nul by default).

file -- "$(whence -p zsh)"

Would be more correct (which in zsh being an alias for whence -c, -c for csh, which being originally a csh script for csh users).

In fish, as fish's maintainer already said, command substitution is with (...) or also $(...) in recent versions (the latter can be used inside double quotes).

Contrary to zsh, fish doesn't have which builtin, so the behaviour will vary with the system you're on, which being a non-standard, often broken command.

fish has a type builtin command though which supports a -P option to force a $PATH lookup like zsh's whence -p.

By default command substitutions are split on newline characters. If using $(...) inside double quotes however, there's no splitting but all the trailing newline characters are removed.

So with fish, a more correct version would be:

file -- "$(type -P zsh)"

or with older versions:

file -- (type -P zsh | string collect)

(with one difference from the "$(...)" in that if type produces no output (or only newlines) it will not pass any argument to file instead of one empty argument).


¹ for which zsh to return something ending in newline, you'd need to have done hash zsh=$'/path/to/some/fileendinginnewline\n\n' for instance though, which is quite contrived here.

Stéphane Chazelas
  • 522,931
  • 91
  • 1,010
  • 1,501