fish (简体中文)

From ArchWiki

翻译状态:本文是 Fish翻译。上次翻译日期:2021-05-31。如果英文版本有所更改,则您可以帮助同步翻译。

fish是“friendly interactive shell”的缩写,是一个“交互式的、对用户友好的 命令行shell”。

fish 被有意设计成不完全与 POSIX 兼容。fish 的作者们认为 POSIX 中存在一些缺陷和矛盾,并通过 fish 简化的或不同的语法解决这些问题。因此,即使简单的 POSIX 兼容的脚本也可能需要较多的修改──甚至完全重写──才能在 fish 中运行。

安装

安装 fish 软件包。或者安装开发版的 fish-gitAUR

安装完成这后,输入 fish 即可进入 fish shell。

要查看帮助文档,在 fish 中输入 help,文档将会在浏览器中打开。建议用户至少读完文档中“Syntax overview”这一节内容,因为 fish 的语法和其他 shell 的并不一样。

在系统中配置 fish

需要考虑的是:是把 fish 设置为默认 shell,也就是用户登录时直接进入的 shell,还是通过当前的默认 shell 启动 fish,并只在交互模式下使用 fish。这里,我们假设当前的默认 shell 是 Bash

  • 把 fish 设为默认shell:这种方式需要用户对 fish 的运行机制及其脚本语言有些基本的了解。用户需要把当前的初始化脚本和环境变量移动到 fish 的环境中。#将 fish 设为默认 shell 中有具体的步骤。
  • 只把 fish 用作交互式shell:这种方法“破坏性”较小。Bash 会照常运行所有的初始化脚本,并在此基础上启动 fish。#仅将 fish 用作交互式 shell 中有具体的步骤。

将 fish 设为默认 shell

如果你决定把 fish 设为默认的 shell,首先你需要将你的用户的 shell 改为/usr/bin/fish。参照 Command-line shell#Changing your default shell 中的步骤。

下一步是把 Bash 的几个初始化脚本中的操作和配置用 fish 的方法和工具重写,这些脚本分别有/etc/profile~/.bash_profile/etc/bash.bashrc~/.bashrc

需要特别关注的是,在登录到 fish 中后,你需要尽快检查、调整环境变量$PATH的内容。在 fish 中,$PATH是一个全局环境变量全局表示作用范围包含所有函数,并且会在重启的时候被清除;环境变量则意味着它对 fish 的所有子进程都是可见的。相较于修改直接修改 $fish_user_path,现在更推荐在配置文件中使用 fish_add_path 来设置路径,比如

$ fish_add_path -p /路径_1 /路径_2 /路径_3

会将这三个路径添加到搜索路径的头部。

仅将 fish 用作交互式 shell

如果不将 fish 设为默认 shell,就能照常运行 Bash 的初始化脚本。这能够保证用户当前的环境变量不受影响并且在 fish 中也能使用,因为 fish 是作为 Bash 的子进程运行的。下面是几种只把 fish 用作交互式 shell 的方法。

通过 .bashrc 启动 fish

Tango-inaccurate.pngThe factual accuracy of this article or section is disputed.Tango-inaccurate.png

Reason:exec fish 写在 .bashrc 中或许不是万全的做法,比如这个 bug 报告:https://bugzilla.redhat.com/show_bug.cgi?id=1910424. (Discuss in Talk:Fish (简体中文))

保持默认 shell 为 Bash 不变,然后添加exec fish到合适的 Bash 配置文件中,比如.bashrc。用这种方法,Bash 会正常执行/etc/profile/etc/profile.d中的所有配置文件。相对于后面的几种方法,这种是最通用的,因为这种方法在本机计算机和 SSH 远程计算机上都能使用。

提示:
  • 在这种配置下,如要使用 Bash 而不是接着启动 fish,需要使用bash --norc,以防止Bash加载~/.bashrc之后执行exec fish
  • 要让类似于bash -c 'echo test'中的命令在 Bash 中执行而不是启动 fish,你可以将exec fish换为if [ -z "$BASH_EXECUTION_STRING" ]; then exec fish; fi
  • 在父进程不是 fish 的情况下才起动 fish:这种方法可以在更方便地切换到 Bash 的同时继续使用 ~/.bashrc 中的配置。
    if [[ $(ps --no-header --pid=$PPID --format=cmd) != "fish" ]]
    then
        exec fish
    fi
    

使用终端模拟器的选项

另一种方法是在启动终端模拟器时通过添加命令行选项来启动fish。对于大部分的终端,这个选项是 -e。比如,要打开 gnome-terminal 并运行fish,可以将 gnome-terminal 的快捷方式改为:

gnome-terminal -e fish

对于不支持设置 shell 的终端模拟器,比如lilyterm-gitAUR,则需要这样:

SHELL=/usr/bin/fish lilyterm

另外,有些终端可以在设置或配置文件中将 fish 设为默认 shell。

使用终端复用器的选项

要将 fish 设为 tmux 启动的默认 shell,在~/.tmux.conf中加入这行:

set-option -g default-shell "/usr/bin/fish"

tmux 启动时,就会自动进入 fish。

配置

fish的配置脚本保存在~/.config/fish/config.fish。这个文件与 .bashrc相似,当每次启动时,fish会执行这个文件中的命令。需要注意的是,如果需要设置长期保留的变量,应该将这个变量设为一个全局变量,而不是将它定义在这些配置脚本中。

用户定义的函数保存在~/.config/fish/functions中,文件名为函数名.fish

网页界面

可以在 fish 的交互式网页配置页面中修改配色、提示符、函数、变量、历史记录和快捷键:

$ fish_config

在 IPv6 被完全禁止的环境下,网页配置界面或许不能起动。参见[1]IPv6#Disable IPv6

命令补全

fish 可以通过解析 man 的数据生成自动补全规则,这些自动补全规则被保存在~/.config/fish/generated_completions/。可以运行下面的命令自动更新补齐规则。

$ fish_update_completions

你也可以在 ~/.config/fish/completions/ 目录下放置自己定义的补全规则。/usr/share/fish/completions/ 中有更多的例子。

对 Arch Linux 来说, pacman, pacman-key, makepkg, cower, pbget, pacmatic 这些命令的自动补全规则已经在内置在 fish 中了,因为 fish 在开发的时候就包含了很多命令的补全规则。fish 的内存管理足够智能,不会因为命令补全产生负面影响。

提示与技巧

命令替换

fish 没有 Bash 式的历史记录替换功能(如 sudo !!)。fish 的开发者在 fish faq 中建议使用交互式的方式调用历史记录:方向键可以按整条命令回顾历史,而Alt+上可以回顾命令的单个参数。Alt+S 会在整条命令的最前面加上 sudo

不过,fish wiki 中介绍了一些替代方法。虽然这些方法提供的历史替换功能并不完整,但可用的有 !!(替换上一条命令)和 !$(替换上一条命令的最后一个参数)。

串联命令

在低于 3.0 的版本中,fish 并未实现串联命令符号 &&||,推荐使用 ; and; or 语法来实现相似的效果。也可以参照 fish wiki,设置按键绑定来自动替换符号。

取消问候语

默认情况下,每次启动时 fish 都会打印问候语。如要不显示问候语,可以在 fish 中运行:

set -U fish_greeting ""

让 su 默认启动 fish

如果 su 目标用户的默认 shell 是 Bash,但是你想使用 fish 作为 shell 的话,可以添加一个 su 函数覆盖默认的 su 命令,在切换用户的时候自动使用 fish:

function su
    /bin/su --shell=/usr/bin/fish $argv
end

登录 fish 时自动起动 X

把下面配置添加到 ~/.config/fish/config.fish,即可在登录 tty1 的时候自动启动 X。

# Start X at login
if status --is-login
    if test -z "$DISPLAY" -a $XDG_VTNR = 1
        exec startx -- -keeptty
    end
end

如果在使用 fish 作为交互 shell,需要把例子中的 is-login 换为 is-interactive

在提示符中增加 git 状态

当你处在一个 git 目录中时,如果你想在 fish 的提示符中显示分支和修改的相关信息,可以仿照以下的例子编写 fish_prompt 函数:

~/.config/fish/functions/fish_prompt.fish
function fish_prompt
  set -l last_status $status

  if not set -q __fish_git_prompt_show_informative_status
    set -g __fish_git_prompt_show_informative_status 1
  end
  if not set -q __fish_git_prompt_color_branch
    set -g __fish_git_prompt_color_branch brmagenta
  end
  if not set -q __fish_git_prompt_showupstream
    set -g __fish_git_prompt_showupstream "informative"
  end
  if not set -q __fish_git_prompt_showdirtystate
    set -g __fish_git_prompt_showdirtystate "yes"
  end
  if not set -q __fish_git_prompt_color_stagedstate
    set -g __fish_git_prompt_color_stagedstate yellow
  end
  if not set -q __fish_git_prompt_color_invalidstate
    set -g __fish_git_prompt_color_invalidstate red
  end
  if not set -q __fish_git_prompt_color_cleanstate
    set -g __fish_git_prompt_color_cleanstate brgreen
  end

  printf '%s%s %s%s%s%s ' (set_color $fish_color_host) (prompt_hostname) (set_color $fish_color_cwd) (prompt_pwd) (set_color normal) (__fish_git_prompt)

  if not test $last_status -eq 0
    set_color $fish_color_error
  end
  echo -n '$ '
  set_color normal
end

然而,fish 已经抛弃了这种方式,参见 fish-shell git。作为替代,fish 现在内置了Informative Git Prompt,该功能可以通过 fish_config 启用。

在 SSH 中用彩色显示主机名

如果需要在通过 SSH 登录到 fish 时用不同的颜色标记提示符中的主机名,可以将以下内容添加到函数 fish_prompt 中,或者添加到 fish 的配置文件中。这里以红色为例:

~/.config/fish/functions/fish_prompt.fish
...
if set -q SSH_TTY
  set -g fish_color_host brred
end
...

ssh-agent 问题

在fish中,eval (ssh-agent) 会因为变量的设置方式而报错。一个变通的方案是使用 csh 风格的选项-c

$ eval (ssh-agent -c)

"command not found" 事件函数

fish 包含了一个响应“命令未找到”("command not found")事件的函数 fish_command_not_found。当遇到一个当前系统不存在的命令时,这个函数会使用 pkgfile 在官方仓库中查找哪个软件包拥有这条命令,而如果 pkgfile 不存在则回落使用 pacman -F 作为替代。

从 fish 3.2.2 开始,因为 pacman -F 严重的卡顿问题,所以 fish_command_not_found 不会再使用它作为回落。

可以写一个 fish_command_not_found 函数来替换掉 fish 自带的。比如在“命令未找到”时只打印错误消息,这样就不会有卡顿现象:

$ function fish_command_not_found
      __fish_default_command_not_found_handler $argv[1]
  end

然后保存这个函数:

$ funcsave fish_command_not_found

从 jobs 中删除进程

当退出 fish 的时候,所有后台进程也会终止。为了让一个任务即使在 fish 退出了之后也继续运行,需要先输入 disown 命令再退出。比如说,这个例子在后台起动 Firefox 然后 disown 它:

$ firefox &
$ disown
$ exit

这样即使退出了 fish,Firefox 也会继续运行。请查阅 fishdisown(1) 了解更多细节。

快速设置别名

如果想要快速设置一个持久化的别名(即使 fish 退出也不会失效),可以使用这种方法:

$ alias FooAliasName "foo --option"
$ funcsave FooAliasName

而在 fish 3.0 之后的版本中,alias 增加了 --save(-s) 选项。

$ alias -s FooAliasName "foo --option"

别名会自动保存,这样就不需要再执行 funcsave。如果想查看或者编辑所有的函数,可以运行 fish_config,然后在网页界面的 Function 标签下进行配置。

如要获取更多文档,查阅 alias - create a function — fish-shell

另请参阅