bash-vs-fish 速查
版本unknown 更新日志2025-11-17 GitHubfish-shell/fish-shell
340px

基本与模式

  • bash --version / echo $BASH_VERSION 查看版本
  • help / help cd 查看内建帮助;man bash 手册
  • set -o emacsset -o vi 切换行编辑模式(Readline)
  • shopt -s extglob 启用扩展通配;shopt -s globstar 启用 **
  • set -euo pipefail 更健壮脚本:遇错退出/未定义变量报错/管道传递失败
  • 参考:GNU Bash Reference Manual 5.3(Command Line Editing / Shell Expansions / The Set Builtin / Shell Builtin Commands)。

命令行编辑(Readline)

  • 移动:Ctrl+A 行首;Ctrl+E 行尾;Alt+F/B 词前进/后退;Ctrl+F/B 字符
  • 删除:Ctrl+U 删至行首;Ctrl+K 删至行尾;Alt+D 删到词尾;Ctrl+W 删到词首
  • 撤回/粘贴:Ctrl+Y 粘贴(yank);Alt+Y 轮换 kill-ring;Ctrl+_ 撤销
  • 历史:Ctrl+R 反向搜索;↑/↓ 上/下一条;Alt+. 上条命令最后一个参数
  • 其他:Ctrl+L 清屏;Ctrl+C 中断;Ctrl+D EOF(空行时退出)
  • 以上为 Emacs 模式常用;set -o vi 后提供 vi 风格按键。

变量与声明

  • name=value 赋值(等号左右无空格);readonly name 只读
  • declare -i n=0 整型;declare -a arr 索引数组;declare -A map 关联数组
  • local x 函数内局部变量;export x 传递到子进程环境
  • 特殊参数:$0 $1..$9 ${10} 位置参;$# 个数;$@/$* 全部;$$ PID;$! 后台 PID;$? 上一退出码;$- 选项
  • "$@" 逐参保留分隔;"$*" 合并为单字符串(由 IFS 连接)。

参数展开

  • 默认/赋值/报错/替代:${v:-word} 默认;${v:=word} 置默认;${v:?msg} 未设报错;${v:+alt} 已设用 alt
  • 长度/子串:${#v} 长度;${v:offset[:len]} 子串;数组:${arr[@]:o:l} 切片
  • 模式删除:${v#pat}/${v##pat} 去前缀(最短/最长);${v%pat}/${v%%pat} 去后缀
  • 替换:${v/pat/repl} 首次;${v//pat/repl} 全部;${v/#pat/repl} 首部;${v/%pat/repl} 尾部
  • 大小写:${v^}/${v^^} 首字母/全部大写;${v,}/${v,,} 小写
  • 间接展开:${!name} 以变量值作为变量名再取值;关联数组键:${!map[@]}
  • 花括号展开(非参数):{1..5}{a..e}{x,y,z}(在引号外生效,发生于路径名展开之前)

字符串与算术

  • 算术:$(( expr ));自增:((i++));比较:((a < b)) 返回状态
  • 命令替换:$(cmd)(推荐)/ `cmd`(旧);进程替换:<(cmd) / >(cmd)
  • 转义字符串:$'\n' C 风格转义;ANSI-C 引用
  • 剪裁空白:x=${x##*( )} 去前空格;x=${x%%*( )} 去后空格(需 shopt -s extglob
  • 引用规则:单引号完全字面量;双引号保留空格并允许 $/$( ) 展开;$'..' 允许转义

条件与测试

  • 传统 [ ]:字符串:=/!=;整数:-eq -ne -lt -le -gt -ge;文件:-f -d -e -s -r -w -x
  • 增强 [[ ]]:支持模式匹配 == pat;正则 =~;短路与 &&/||
  • [[ ]] 内不进行路径名扩展与单词分割,推荐在 Bash 使用。

正则匹配([[ =~ ]])

  • if [[ $s =~ ^([0-9]+)-([a-z]+)$ ]]; then echo ${BASH_REMATCH[1]} ${BASH_REMATCH[2]}; fi
  • 注意:右侧正则不加引号;使用小括号捕获;匹配失败返回非 0

循环与分支

  • for x in a b; do ...; done;C 风格:for((i=0;i<n;i++)); do ...; done
  • while read -r line; do ...; done < file 安全读取行
  • case $x in pat) ...;; *) ...;; esac 模式分支;可配 shopt -s nocasematch 忽略大小写
  • break/continue;带层级:break 2

读写与格式化

  • read -r line 禁止反斜杠转义;read -a arr 读入数组
  • read -n 1 -s key 无回显读取 1 字符;read -t 5 var 超时
  • mapfile -t arr < file 按行读入数组;mapfile -t -n 10 arr < file 只读 10 行
  • printf 'x=%q\n' "$x" 安全转义输出;printf -v out '%04d' 7 直接赋值变量
  • 常用格式:%s 字符串;%q 可重用转义;%d 整数;%f 浮点;\n 换行

解析参数(getopts)

  • while getopts ":ab:c" opt; do
      case $opt in
        a) flag_a=1 ;;
        b) arg_b=$OPTARG ;;
        c) flag_c=1 ;;
        :) echo "缺少参数: -$OPTARG"; exit 2 ;;
        \?) echo "未知选项: -$OPTARG"; exit 2 ;;
      esac
    done
    shift $((OPTIND-1))
  • : 前缀开启“缺参”错误处理;带冒号的选项声明为“需要参数”

函数

  • 定义:name() { local x; ...; }function name { ...; }
  • 返回:return N(0 成功);输出用 echo/printf 传递
  • 参数:$1…;全部:"$@";个数:$#

数组

  • 索引:arr=(a b c)arr[2]=X;取值:${arr[0]}/${arr[@]}
  • 长度:${#arr[@]};下标:${!arr[@]};切片:${arr[@]:1:2}
  • 关联:declare -A map; map[foo]=bar;键:${!map[@]};值:${map[@]}

重定向与管道

  • 标准形式:>/>> 输出;< 输入;n>file 指定 fd
  • Here 文档:cat <<EOF ... EOF(引用定界符可关闭展开);Here 字符串:cmd <<< "$str"
  • 合并输出:2>&1;Bash 专有:&>file(stdout+stderr 到文件)
  • 进程替换:diff <(cmd1) <(cmd2);需要 bash 或兼容实现
  • 管道失败传播:set -o pipefail

文件描述符高级

  • exec 3<> file 打开 fd3 读写;echo hi >&3 写入;read -u 3 x 从 fd3 读取
  • exec 3>&- 关闭 fd3;cmd >&- 关闭 stdout;cmd 2>&- 关闭 stderr

作业控制

  • cmd & 后台;jobs 查看;fg %1/bg %1 前/后台;disown %1 脱离
  • Ctrl+Z 挂起;kill -SIGCONT %1 继续
  • coproc name { ...; } 协程进程(创建管道 FD)
  • 非交互模式需 set -m 开启作业控制。

历史与别名

  • history / history -c 清空;!n 第 n 条;!! 上一条;!$ 上条最后参数
  • alias ll='ls -alh' 定义;unalias ll 取消;type -a cmd 查看解析顺序
  • HISTCONTROL=ignoredups:erasedupsHISTSIZE/HISTFILESIZE 控制历史大小
  • shopt -s histverify 执行前可编辑 !... 展开结果;fc -e - 打开编辑历史

调试与陷阱

  • bash -n script.sh 语法检查;bash -x script.sh 跟踪执行
  • set -x/set +x 运行时开启/关闭跟踪;PS4='+$LINENO: ' 行号提示
  • trap 'cleanup' EXIT 退出清理;trap 'handler' ERR 错误陷阱
  • trap 'echo SIGINT' INTtrap -p 查看;trap - INT TERM 清除
  • set -e 遇错即退:谨防与条件判断/管道组合引发的误退,可配合 || true 等模式

扩展通配与递归匹配

  • shopt -s extglob@(p1|p2) 其一;?(p) 0/1 次;*(p) 任意次;+(p) ≥1 次;!(p)
  • shopt -s globstar** 递归匹配目录(如 **/*.sh

可编程补全

  • _foo() { COMPREPLY=( $(compgen -W "start stop restart" -- "${COMP_WORDS[COMP_CWORD]}") ); }
  • complete -F _foo foo 为命令 foo 安装补全;complete -r foo 移除
  • 详见手册 Programmable Completion / Builtins

命令解析顺序

  • 优先级:关键字(保留字)→ 别名 → 函数 → 内建 → 可执行文件
  • command ls 跳过别名/函数调用外部;builtin echo 强制内建;enable -n echo 禁用内建
  • hash -r 清空命中缓存;type -a 显示解析路径

作用域与子 shell

  • ( list ) 在子 shell 执行(变量修改不回传);{ list; } 在当前 shell(末尾必须分号或换行)
  • source file/. file 在当前 shell 读取执行(共享作用域)

常用 shopt 选项

  • nullglob 空模式展开为零参数;dotglob 匹配点文件;nocaseglob 忽略大小写
  • failglob 无匹配时报错;lastpipe 管道最后一个在当前 shell 运行(非交互)
  • checkwinsize 命令后自动更新 LINES/COLUMNScdspell/dirspell 轻微拼写修复
  • cmdhist/lithist 合并多行命令到历史、保留换行;expand_aliases 在非交互中启用别名

Fish 基本与模式

  • fish --versionhelp 打开文档;man fishfish_config Web 配置
  • 交互特性:语法高亮、自动建议、历史提示
  • 逻辑:and/or/not(无 &&/||/!

Fish 变量与作用域

  • set name value;作用域:set -l 局部、-g 全局、-U 通用、-x 导出
  • 列表变量;索引从 1 开始:$var[1];长度:count $var
  • 参数:$argv;追加:set var $var new;删除:set -e var

Fish 命令替换与字符串

  • 命令替换:set files (ls *.md)(圆括号);子串:string sub -s 2 -l 3 $s
  • 分割/合并:string split , $sstring join , $list;修剪:string trim
  • 匹配/替换:string match -r '^[0-9]+'string replace -r '(foo)' 'bar'
  • 算术:math '1+2*3'(无 $(( ))

Fish 条件与循环/分支

  • 条件:if test -f file; ...; else if test -d dir; ...; else; ...; end
  • 循环:for x in $list; ...; endwhile CONDITION; ...; end
  • 分支:switch $x; case pat; ...; case '*'; ...; end

Fish 函数/补全/缩写

  • 函数:function hello; echo hi $argv; end;列出:functions
  • 补全:complete -c foo -a "(seq 1 5)" -d 'numbers';查看:complete -C foo
  • 缩写:abbr -a gs 'git status';列出:abbr -s

Fish 重定向与管道

  • cmd > outcmd >> outcmd 2> err;(部分版本支持 ^ 表示 stderr)
  • cmd | othercmd |& other(合并输出)
  • 管道状态:echo $pipestatus(列表,存放各段退出码)

Fish PATH 与环境

  • PATH 是列表:set -x PATH /opt/bin $PATH(无需冒号拼接)
  • 配置:~/.config/fish/config.fish;通用变量:set -U var val

Fish 常用内建

  • statustypecontainspathcounttimereadargparse

Bash vs Fish 关键差异

  • 命令替换:Bash $(cmd);Fish (cmd)
  • 逻辑运算:Bash &&/||/!;Fish and/or/not
  • 条件语法:Bash [[ ]]/[ ];Fish 使用 if/test(无 [[ ]]
  • 算术:Bash $(( ));Fish math
  • 变量模型:Bash 标量+数组;Fish 列表(索引从 1),$argv 为全部参数
  • 作用域与导出:Bash local/export;Fish set -l/-g/-U/-x
  • 函数与别名:Bash alias;Fish 推荐 abbr 与函数
  • PATH:Bash 用冒号拼接;Fish 用列表 set -x PATH /path $PATH
  • 历史:Bash !!/!$ 历史展开;Fish 用自动建议与 history
  • 补全:Bash complete/compgen;Fish complete(语法不同)
  • 管道状态:Bash 需 set -o pipefail;Fish $pipestatus
  • 重定向:Bash 2>/&>;Fish 常用 2>(部分版本支持 ^
  • 块与结束:Bash do ... done;Fish end
  • 正则与字符串:Bash [[ =~ ]];Fish string match/replace -r
  • 索引:Bash 从 0(数组)/1(位置参);Fish 统一从 1
  • 配置:Bash ~/.bashrc;Fish ~/.config/fish/config.fish

参考与来源

  • GNU Bash Reference Manual 5.3(参数展开、重定向、可编程补全、作业控制等章节)
  • Fish Shell Documentation(current):Tutorial / Commands / Language / Variables / Functions / Completion / Abbreviations
  • 本目录 source.html 为原始素材(已据此迁移与审校)
  • 本目录 refmap.md 列出章节映射与修改说明