fish shell tutorial
June 4, 2016
Tools
在fish里面,相邻的列表或者字符串会按照笛卡尔积来展开。比如:
liu@lisp /p/tmp> echo {good,bad}" apples"
good apples bad apples
liu@lisp /p/tmp> set -l a x y z
liu@lisp /p/tmp> echo $a
x y z
liu@lisp /p/tmp> set -l b 1 2 3
liu@lisp /p/tmp> echo $a$b
x1 y1 z1 x2 y2 z2 x3 y3 z3
liu@lisp /p/tmp> echo $a"-"$b
x-1 y-1 z-1 x-2 y-2 z-2 x-3 y-3 z-3
liu@lisp /p/tmp> echo {x,y,z}$b
x1 y1 z1 x2 y2 z2 x3 y3 z3
liu@lisp /p/tmp> echo {$b}word
1word 2word 3word
liu@lisp /p/tmp> echo "$b"word
1 2 3word
上面用到了括号扩展的功能:在一个大括号里面以逗号分隔的几个字符串会被扩展成列表里 面的每个元素被当作一个新的变量。比如:
liu@lisp /p/tmp> echo input.{c,h,txt}
input.c input.h input.txt
liu@lisp /p/tmp> mkdir src
liu@lisp /p/tmp> touch input.{c,h,txt}
liu@lisp /p/tmp> mv *.{c,h} src
liu@lisp /p/tmp> ls src
input.c input.h
在其它shell里面,一般都使用\
`来作为命令替换,在fish里面,就不一样了。它使用的
是括号。比如:
liu@lisp /p/tmp> echo In (pwd), running (uname)
In /private/tmp, running Darwin
liu@lisp /p/tmp> set os (uname)
liu@lisp /p/tmp> echo $os
Darwin
其实这种方法变得更简单直接。不过,需要注意的是,这种方法不能使用在双引号里面,它 并不会在双引号里面自动把命令替换成命令的输出结果。弥补的做法只能是这样:
liu@lisp /p/tmp> touch "testing_"(date +%s)".txt"
liu@lisp /p/tmp> ls te*
testing_1465007369.txt
也就是说拼接一下就好了。
另外,一个经常用到的功能就是,不像其它shell那样使用&&
和||
来作为逻辑与和逻辑
或,fish里面用的是英语单词and
, or
, not
,比如:
liu@lisp /p/tmp> touch file1.txt
liu@lisp /p/tmp> cp file1.txt file1_bak.txt; and echo "Backup successful"; or echo "Backup failed"
Backup successful
liu@lisp /p/tmp> cp file2.txt file1_bak.txt; and echo "Backup successful"; or echo "Backup failed"
cp: file2.txt: No such file or directory
Backup failed
然而,中间还是要加分号分割。
经常用到的,条件语句这样使用:
liu@lisp /p/tmp> if grep fish /etc/shells
echo Found fish
else if grep bash /etc/shells
echo Found bash
else
echo Got nothing
end
/usr/local/bin/fish
Found fish
显然比其它的shell的语法自然好看多了 :joy: 即使你在终端里面直接输入这些代码,它也 会自动锁进。
同样有简单的switch语句:
liu@lisp /p/tmp> switch (uname)
case Linux
echo Hi Linux!
case Darwin
echo Hi Mac OS X!
case FreeBSD NetBSD
echo Hi BSD
case '*'
echo Hi, stranger!
end
Hi Mac OS X!
不像其它的shell一样使用alias和用特殊的提示符语法之类的……这些事情,在fish里面用函 数替换,变得更简单好用。
函数的参数是放在变量$argv
里面的,而不是像别的shell那样放在奇怪的$1
里面:
liu@lisp /p/tmp> function say_hello
echo Hello $argv
end
liu@lisp /p/tmp> say_hello
Hello
liu@lisp /p/tmp> say_hello everyone!
Hello everyone!
functions
这个关键字,可以列出所有的函数。有趣的是在这个关键字后面加一个函数的
名字可以看到这个函数的源代码:
liu@lisp /p/tmp> functions
., N_, abbr, alias, cd, contains_seq, delete-or-exit, dirh, dirs,
down-or-search, eval, export, fish_config, fish_default_key_bindings,
fish_fallback_prompt, fish_indent, fish_mode_prompt, fish_prompt,
fish_sigtrap_handler, fish_update_completions, fish_vi_cursor,
fish_vi_key_bindings, fish_vi_mode, fisher, funced, funcsave, grep, help,
history, hostname, imgcat, isatty, it2dl, iterm2_fish_mode_prompt,
iterm2_fish_prompt, iterm2_precmd, iterm2_preexec, iterm2_prompt_end,
iterm2_prompt_start, iterm2_set_user_var, iterm2_status, la, ll, ls, man,
math, nextd, nextd-or-forward-word, open, popd, prevd, prevd-or-backward-word,
prompt_pwd, psub, pushd, realpath, say_hello, seq, setenv, suspend, trap,
type, umask, underscore_change, up-or-search, vared,
liu@lisp /p/tmp> functions fish_vi_mode
function fish_vi_mode
echo 'The `fish_vi_mode` function is deprecated.' >&2
echo 'Please switch to calling `fish_vi_key_bindings`.' >&2
# Turn on vi keybindings
set -g fish_key_bindings fish_vi_key_bindings
end
for
循环也很简单直接:
liu@lisp /p/tmp> for file in *.txt
cp $file $file.bak
end
liu@lisp /p/tmp> ls *.txt; ls *.bak
errors.txt file1_bak.txt testing_1465007369.txt
file1.txt input.txt
errors.txt.bak input.txt.bak
file1.txt.bak testing_1465007369.txt.bak
file1_bak.txt.bak
seq
这个命令加一个数字n
可以生成从1到n
的一个列表,比如:
liu@lisp /p/tmp> seq 5
1
2
3
4
5
liu@lisp /p/tmp> for x in (seq 5)
touch file_$x.txt
end
liu@lisp /p/tmp> ls file_*
file_1.txt file_2.txt file_3.txt file_4.txt file_5.txt
在别的shell里面,一般用$PS1
这个变量来显示提示符,在fish里面,使用的是一个名字
叫做fish_prompt
的函数,这个函数的输出就作为提示符,比如,你可以像下面这样定制
自己的提示符:
liu@lisp /p/tmp> function fish_prompt
echo "Sabastian Bach >> "
end
Sabastian Bach >>
可以通过set_color
来设置颜色,可以把ANSI
colors或者RGB的16进制的值
作为参数:
Sabastian Bach >> function fish_prompt
set_color purple
date "+%m/%d/%y"
set_color FF0
echo (pwd) '> '
set_color normal
end
06/04/16
/private/tmp >
fish里面有一种全局变量,这个变量在fish里面通用,只要设置一次,那么即使重启,这个
变量依然存在。比如这样设置EDITOR
这个变量:
fish $ set -U EDITOR vim
fish $ echo $EDITOR
vim
打开另外一个终端这个变量依然有效。
另外有意思的一点是,其它shell里面,你一般会在类似于~/.bashrc
或者~/.zshrc
里面
设置$PATH
的值,当然,在fish里面,你也可以这样做。比如你可以在
~/.config/fish/config.fish
这个文件里面设置。如果没有这个文件就需要创建一个。其
实它就相当于bash里面的~/.bashrc
。你可以在这个文件里面定义一些函数,或者设置一
些变量。比如:
> cat ~/.config/fish/config.fish
set -x PATH $PATH /sbin/
function ll
ls -lh $argv
end
然而,在fish里面更高效和更常见的做法是,使用自动加载函数和上面提到的全局变量。
所谓自动加载函数,就是在~/.config/fish/functions/
目录里面以命令的名字加上后缀
.fish
作为文件名新建一个文件。比如你可以定义你自己的提示符:
> cat ~/.config/fish/functions/fish_prompt.fish
function fish_prompt
echo (pwd) "> "
end
其实函数对应的文件可以自动创建,参见 funced和 funcsave
为了说明这个用法,比如我在终端直接输入:
Pictures $ function gopi
ssh aries@pi
end
Pictures $ funcsave gopi
然后以后就可以使用gopi
这个命令了……是不是很简单。根本不用打开一个什么文件什么的。
(这个命令是用来登陆我的树莓派的…… :joy: )
fish有一个插件特别好用,类似于zsh的oh-my-zsh
。Github地址是:fisherman
安装的时候,可能会因为git.io
被墙安装失败。其实,只要把git.io/fisher
里面的内
容复制到~/.config/fish/functions/fisher.fish
文件里面就可以了。没有这个文件就新
建一个。
然后就可以愉快地使用了。
经常会遇到设置环境变量的情况,可以用paths
这个插件愉快地搞定。比如我给$PATH
追加一个变量,可以这样做:
mkdir $paths_config/PATH
echo ~/bin > $paths_config/PATH/HOMEBIN
然后重新启动fish才有效。这一点很重要!
总的来说,仿佛fish和bash、zsh之类的差别很大,然而用过之后才知道,其实它变得更简 单并且,更好配置,去掉了别的shell里面那些乱七八糟奇怪的语法,符合更常用的语言的 语法规则,所以,我所有的*nix环境都换成了fish :joy: 棒棒哒
日常锻炼
6月1日,5.27km
坚持这件事情,与其说靠的是毅力,不如说是惯性。人的毅力靠不住,习惯却很顽固。
6月2日,5.26km
今天跑步听的是友聊FM最新一期《闲聊飞了》,聊的是小米无人机,同样,不知不觉,就跑 完了……
6月3日,5.21km
最近看周易,跑步的时候默背分宫卦象图 :joy: 我是有……多无聊。。。
6月4日,5.17km
Android版的Google拼音输入法比搜狗好用并且好看多了……