fish为何不支持POSIX标准?是否应从Bash切换至fish?
我来帮你理清楚这两个问题——作为长期用fish又偶尔切回Bash的人,太懂这种纠结了😉
POSIX是一套shell的标准规范,Bash是兼容POSIX的同时又扩展了很多特性,而fish从设计之初就没打算完全遵循POSIX,而是走了更易用的路线,具体差异主要体现在这些地方:
变量与数组语法差异极大
POSIX里变量赋值是var=value(不能有空格),引用用$var;而fish必须用set var value来赋值,数组更是完全不同:POSIX数组是arr=(a b c),访问元素用${arr[0]}(索引从0开始),fish里是set arr a b c,访问用$arr[1](索引从1开始),而且fish的数组处理更像高级语言,但和POSIX完全不兼容。条件判断与循环语法不同
POSIX里的if [ $a -gt 5 ]或者if [[ $a == "foo" ]],fish虽然支持部分类似写法,但细节差异很大;比如POSIX的算术判断(( a > b )),fish里要写成if test $a -gt $b或者if [ $a -gt $b ];循环的话,POSIX的for i in $(ls); do ... done,fish里是for i in (ls); ... end(用end代替done),而且不支持POSIX里一些复杂的循环嵌套写法。现成POSIX脚本无法直接运行
这是最实际的问题:系统里的很多配置脚本、开源项目的configure脚本、各种运维脚本都是基于POSIX写的,直接在fish里运行./script.sh大概率会报错,因为fish不认识里面的POSIX语法。解决办法是用bash script.sh来运行,或者脚本开头有#!/bin/bash的话,系统会自动调用Bash执行,不会影响。函数与特殊变量不一样
POSIX的函数定义是myfunc() { ... },fish里是function myfunc; ... end;特殊变量方面,POSIX的$?(上命令退出码)在fish里是$status,$$(当前PID)是$pid,$IFS(字段分隔符)在fish里几乎没用,因为它用自己的方式处理字段分割。
这个没有绝对的答案,得看你的使用场景:
适合换的情况
- 日常交互为主:如果你平时用shell就是浏览文件、敲命令、简单的命令拼接,fish的自动补全、语法高亮、智能提示真的能大幅提升效率,而且开箱即用,不用像Zsh那样装一堆插件配置半天。
- 不依赖大量POSIX脚本:如果你很少自己写脚本,或者运行脚本时愿意切换到Bash,那完全可以换,毕竟交互体验提升太多了。
- 喜欢直观的语法:fish的语法更贴近Python这种高级语言,比如数组操作、条件判断都很直观,学习成本低,新手也能快速上手。
不适合换的情况
- 需要编写/维护POSIX脚本:如果你的工作经常要写兼容多系统的脚本,那Bash的兼容性更好,而且平时用Bash的话,语法习惯更统一,不会在写脚本时混淆fish和POSIX的语法。
- 依赖Bash专属特性:比如Bash的进程替换
<(command)、here string<<<、[[ ]]的正则匹配这些特性,fish虽然有替代方案,但用起来麻烦,需要重新学习。
折中方案
其实完全可以把fish设为默认shell,平时享受它的交互便利,遇到需要Bash的场景时,直接输入bash切换到Bash环境就行,或者给脚本加#!/bin/bash开头,系统会自动用Bash执行,这样两全其美。
内容的提问来源于stack exchange,提问作者Cy8099

