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通用公共許可證