Fish

fish (friendly interactive shell) 是一个Unix shell。fish旨在成为一个比其他shell交互性更强、用户体验更好的shell，并让其丰富的强大功能能够被用户轻松发现、记住并学以致用。fish的语法既不衍生于Bourne shell也不衍生于C Shell，故被分类为一种“外来”shell。有别于为节约系统资源而默认禁用部分功能的其他shell，fish的全部功能都是默认启用的。

## 特色

fish能根据用户的输入历史与当前所在的目录提供实时的自动完成。与bash的类似功能Ctrl历史搜索相比，这种不必频繁切换模式还可以用方向键选择建议项的做法使用户能更流畅地进行输入。

fish也拥有功能丰富的Tab补全功能。fish能够自动补全文件路径、变量与不少命令的参数，且支持路径通配符与C shell的花括号展开。fish某种意义上会通过分析命令的man文档生成与之相关的补全。

fish倾向于使用命令来代替语法结构。由于命令相比语法结构能更方便地在shell内查到帮助内容，fish的功能很容易在使用过程中被用户自行发现。fish允许子程序提供对自己的说明，进一步免去了来回查找帮助的麻烦。fish还允许用户在拥有图形用户界面的浏览器内查看帮助。

## 语法

fish的语法类似于其他兼容POSIX的shell，但由于其开发者认为POSIX shell设计得有问题，fish的语法又与POSIX shell有相当的不同。

```fish
# 下面演示将"bar"赋给变量foo的过程。
# 由于等号在其他shell中会根据两端是否有空格而表现出不同的行为。
# fish选择了不在赋值中使用这个令人困惑的符号。
# set命令也可以用于数组，并可以轻松设置变量的作用域。
> set foo bar
> echo $foo
bar
```

```fish
# 下面演示使用命令替换将pwd的输出赋给wd的过程。
# 由于重音符（`）视觉上容易与单引号混淆且左右的符号都相同的话难以嵌套。
# fish改为在命令替换中使用括号。
# 但与ksh不同的是，fish仅在变量展开中使用美元符号。
> set wd (pwd)
> echo $wd
~
```

```fish
# 将一个有五个元素的数组赋给A。
> set A 3 5 7 9 12
# 将A的第二个和第一个元素赋给B。
# fish的数组索引从1开始。
> set B $A[2 1]
> echo $B
5 3
# 可以用命令替换索引数组中的元素……
> seq 3
1
2
3
> echo $A[(seq 3)]
3 5 7
# ……也可以用其他数组来索引。
# 下面将从A中删除第五个与第三个元素。
> set --erase A[$B]
> echo $A
3 5 9
```

```fish
# for循环。
> for i in *.jpg
      convert $i (basename $i .jpg).png
  end
# 分号可以代替换行来标记语句的结束：
> for i in *.jpg; convert $i (basename $i .jpg).png; end
# 但由于fish也会在历史记录中分开记录每一行，使用换行一般更加方便。
 
```

```fish
# while循环。
# 下面演示输出/etc/passwd中每行的第五个字段。
> cat /etc/passwd | while read line
      set arr (echo $line|tr : \n)
      echo $arr[5]
  end
```

### 没有子shell

其他shell的部分语法结构，例如管道、子程序与循环，是使用一种称为子shell的方式实现的。所谓子shell即临时执行一个用完即退出的新的子进程。在子shell中执行的修改通常不符合一般人的直觉地，不会反映到真正的shell上（即没有函数副作用）。fish不依赖于子shell实现其语法结构，故所有内置命令在任何语境下都是会正常运作的。

```fish
# 相当多的shell会将read在单独的子shell中执行，所以如下语句在他们中无法按预期工作。
# 在常见shell中，bash的管道右边无法产生副作用，故line并不会被赋值。
# ksh中line会被正确赋值，但管道左边的语句无法产生副作用。
# 但是fish和zsh一样，允许管道两边都能有副作用。
> cat *.txt | read line
```

例如，下述bash脚本会因为子shell而导致循环内部给found赋的值在退出循环后即消失

```bash
found=''
cat /etc/fstab | while read dev mnt rest; do
  if test "$mnt" = "/"; then
    found="$dev"
  fi
done
```

而需要用另一种方式来规避这个问题：

```bash
found=''
while read dev mnt rest; do
  if test "$mnt" = "/"; then
    found="$dev"
  fi
done < /etc/fstab
```

如下的fish脚本则无需担心子shell会影响副作用。

```fish
set found ''
cat /etc/fstab | while read dev mnt rest
  if test "$mnt" = "/"
    set found $dev
  end
end
```

## 有用的错误信息

fish会在发生错误时清晰地指出出错的位置并给出修正的方法。

```fish
> foo=bar
fish: Unknown command “foo=bar”. Did you mean “set VARIABLE VALUE”?
For information on setting variable values, see the help section on
the set command by typing “help set”.
# fish：未知的命令"foo=bar"。你是想输入"set 变量 值"吗？
# 关于如何设置变量的值，请输入"help set"获取set命令的帮助。

> echo ${foo}bar
fish: Did you mean {$VARIABLE}? The '$' character begins a variable
name. A bracket, which directly followed a '$', is not allowed as a
part of a variable name, and variable names may not be zero characters
long. To learn more about variable expansion in fish, type “help
expand-variable”.
# fish：你是想输入{$变量}吗？美元符号"$"仅用于变量名开头，而美元符号后的
# 那个花括号并不能作为变量名称的一部分，且变量也不能没有名称。
# 关于如何展开变量，请输入"help expand-variable"。

> echo $(pwd)
fish: Did you mean (COMMAND)? In fish, the '$' character is only used
for accessing variables. To learn more about command substitution in
fish, type “help expand-command-substitution”.
# fish: 你是想输入(命令)吗？在fish中，美元符号"$"仅用于访问变量。
# 关于如何进行命令替换，请输入"help expand-command-substitution"。
```

## 通用变量

Fish有一个名为通用变量的功能。通过利用通用变量，用户可以在多个同时运行的fish实例之间共享一个变量。即使用户注销或者计算机重启，通用变量的值也不会丢失。

```fish
# 将emacs设置为默认文本编辑器。
# "--universal"或者"-U"表示设置通用变量。
> set --universal EDITOR emacs

# 如下命令会把fish的提示符中的当前目录变成蓝色。
# 不止执行命令的这个fish，现在所有正在运行的fish的提示符都会发生变化。
> set --universal fish_color_cwd blue
```

## 其它功能

- 高级tab补全
- 带完备的错误检查的语法高亮
- 支持X Window系统的剪贴板
- 基于terminfo的智能终端处理
- 可搜索的命令行历史记录

第二版还加入了如下功能：

- 自动补全
- 支持256色
- 基于网页的配置功能
- 能够提升性能的更多的内置命令

## 另请参阅

- Unix shell
- shell对比

## 外部链接

- 项目主页
- GitHub上的 fish仓库
- Gitorious上的 fish仓库（不再使用）
- SourceForge上的 fish仓库（不再使用）
- Fish-users - fish用户的综合讨论列表
- bash到fish的命令翻译对照

## 基本资料

- 日语原文：fish
- 作者：Axel Liljencrantz
- 开发者：fish-shell developers
- 发行日期：2005年2月13日
- 操作系统：Unix-like
- 类型：Unix shell
- 许可协议：GNU通用公共许可证