在开源鸿蒙PC运行一款漂亮的shell工具—Fish(Friendly Interactive Shell)
本文记录了使用命令 OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh 构建 Fish Shell 3.7.1 的完整过程。Fish 是一个现代化的交互式命令行 shell,提供语法高亮、智能补全等开箱即用的功能。构建过程包括环境配置、源码下载、补丁应用(如终端属性替换)、CMake配置(指定PCRE2和Curses路径)、编译安装等关键
·
本文记录使用命令 OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh 构建 Fish Shell 3.7.1 的完整过程,包括环境、构建链路、关键日志、常见问题与解决方案、产物验证与重建方法,便于复现与运维。
📖 Fish 简介
Fish(Friendly Interactive Shell)是一个用户友好的命令行 shell,专为交互式使用而设计。Fish 提供了开箱即用的语法高亮、自动补全、智能提示等功能,无需复杂配置即可获得出色的用户体验。
🎯 Fish 的作用与重要性
Fish 是现代化命令行交互的核心工具,提供了:
- 用户友好的交互式 Shell:专为提升用户体验而设计
- 语法高亮:实时语法高亮,帮助识别命令、参数和错误
- 自动补全:智能自动补全,基于命令历史和上下文
- 智能提示:基于命令历史的智能提示和建议
- 无需配置:开箱即用的优秀体验,无需复杂配置
- 现代化语法:简洁直观的语法,易于学习和使用
- 跨平台:支持多种操作系统和平台
🔧 Fish 核心特性
1. 语法高亮
- 实时高亮:输入时实时显示语法高亮
- 错误提示:无效命令和语法错误会以红色显示
- 命令识别:有效命令以不同颜色显示
- 参数高亮:参数和选项以不同颜色区分
2. 自动补全
- 智能补全:基于命令历史和上下文的智能补全
- 描述提示:补全选项显示描述信息
- 多选支持:支持多个补全选项
- 上下文感知:根据当前命令和位置提供相关补全
3. 命令历史
- 增量搜索:输入时自动搜索历史命令
- 智能排序:根据使用频率和相关性排序
- 历史共享:多个 fish 会话共享命令历史
- 历史过滤:支持按命令、时间等过滤历史
4. 配置与定制
- 配置文件:
~/.config/fish/config.fish- 用户配置文件 - 函数目录:
~/.config/fish/functions/- 自定义函数 - 补全目录:
~/.config/fish/completions/- 自定义补全 - 主题定制:支持丰富的提示符主题
5. 辅助工具
fish_indent:Fish 脚本格式化工具fish_key_reader:键盘输入测试工具fish_config:交互式配置工具(Web 界面)
🚀 构建入口与环境
- 📝 执行命令:
OHOS_ARCH=aarch64 OHOS_ABI=arm64-v8a sh ./create-hnp.sh - 🔧 入口脚本:
create-hnp.sh- 检查必需的环境变量
OHOS_ARCH和OHOS_ABI - 导出
LC_CTYPE、TOOL_HOME、OHOS_SDK_HOME - 执行
make -C build-hnp
- 检查必需的环境变量
- 📦 顶层构建:
build-hnp/MakefilePKGS变量定义需要构建的包列表(包含fish)- 通过
check-pkgs机制自动检测PKGS变化并触发重新构建 - 自动合并
external-hnp目录下的外部 HNP 包 base.hnp依赖所有包的.stamp和外部 HNP 包- 总目标
all: copy,打包base.hnp并拷贝到entry/hnp/$(OHOS_ABI)
⚙️ Fish 包的构建配置
- 📁 包目录:
build-hnp/fish/Makefile- 继承通用规则:
include ../utils/Makefrag - 源地址:
https://github.com/fish-shell/fish-shell/releases/download/3.7.1/fish-3.7.1.tar.xz - 版本:
3.7.1
- 继承通用规则:
- 🔧 补丁应用:
- 终端属性替换:将源码中的
TCSANOW替换为TCSADRAINsrc/reader.cpp、src/fish_key_reader.cpp、src/builtins/fg.cpp- 规避在 OHOS 环境下终端属性立即生效可能导致的异常
- CMake 修复:应用
0001-fix-cmake.diff修复 CMake 配置问题 getpwuid适配:应用0002-fix-getpwuid.diff修复getpwuid相关问题
- 终端属性替换:将源码中的
- ⚙️ CMake 配置参数:
-DSYS_PCRE2_LIB="$(shell pwd)/../sysroot/lib/libpcre2-32.so"- PCRE2 32位库路径(fish 需要32位版本处理宽字符)-DSYS_PCRE2_INCLUDE_DIR="$(shell pwd)/../sysroot/include"- PCRE2 头文件路径-DCMAKE_DISABLE_FIND_PACKAGE_Curses=ON- 禁用 Curses 自动查找-DCURSES_FOUND=ON- 手动指定 Curses 已找到-DCURSES_INCLUDE_DIRS="$(shell pwd)/../sysroot/include;$(shell pwd)/../sysroot/include/ncursesw"- Curses 头文件路径-DCURSES_LIBRARY=ncursesw- Curses 库名称-DCURSES_TINFO="$(shell pwd)/../sysroot/lib/libtinfow.so"- Tinfo 库路径-DCMAKE_EXE_LINKER_FLAGS="-L $(shell pwd)/../sysroot/lib -lpcre2-32"- 链接标志,添加-lpcre2-32-DCMAKE_CXX_FLAGS="-DTPUTS_USES_INT_ARG=1"- C++ 编译标志-DFISH_USE_SYSTEM_PCRE2=ON- 使用系统 PCRE2-DWITH_GETTEXT=OFF- 禁用 gettext-DBUILD_DOCS=OFF- 禁用文档构建
- 🔨 构建流程:
- 下载源码包(从 GitHub releases)
- 解包并进入
temp/fish-3.7.1目录 - 应用补丁(TCSANOW→TCSADRAIN、CMake 修复、getpwuid 适配)
- 运行
cmake配置构建系统 - 使用
make -j $(nproc)并行编译 - 使用
make install安装 - 删除导致 HAP 安装失败的特殊文件(
..fish) - 复制到
../sysroot并 strip ELF 文件
- 🔧 通用工具链与路径:
build-hnp/utils/MakefragCC/CXX/LD/AR/RANLIB/...均指向 OHOS SDK 的 LLVM 工具链- 下载支持多镜像回退:
wget→curl,主镜像失败时自动尝试备用镜像
📋 关键日志与过程节点
- 📥 下载与解包:
- 从 GitHub Releases 下载
fish-3.7.1.tar.xz - 完成解包并进入
temp/fish-3.7.1目录 - 下载规则支持多镜像回退:
wget→curl兜底
- 从 GitHub Releases 下载
- 🔧 补丁应用:
- 使用
sed将TCSANOW,替换为TCSADRAIN,(3个文件) - 应用
0001-fix-cmake.diff修复 CMake 配置 - 应用
0002-fix-getpwuid.diff修复getpwuid问题
- 使用
- ⚙️ CMake 配置阶段:
CMAKE_SYSTEM_NAME=Linux,CMAKE_SYSTEM_PROCESSOR=aarch64- 编译器与链接器:检测
clang/ld.lld成功 - PCRE2 32位库检测:成功检测到
libpcre2-32.so - Curses 库检测:成功检测到
ncursesw和tinfow - 禁用文档构建:
BUILD_DOCS=OFF
- 🔨 编译与安装:
- 生成
fish、fish_indent、fish_key_reader二进制文件 - 安装大量补全脚本到
share/fish/completions/ - 安装大量函数脚本到
share/fish/functions/ - 安装配置文件到
etc/fish/ - 删除导致 HAP 安装失败的特殊文件(
..fish) - 使用
llvm-strip剥离符号,减小文件大小
- 生成
- 📦 打包:
- 完成
base.hnp重打包,拷贝产物到entry/hnp/arm64-v8a/ - Fish Shell 已成功打包到
base.hnp中
- 完成
✅ 产物验证
📦 检查打包文件
ls build-hnp/base.hnp # 应存在
ls entry/hnp/arm64-v8a/*.hnp # 应包含 base.hnp 与 base-public.hnp
🔍 检查二进制文件
# 检查 Fish 可执行文件
ls -lh build-hnp/sysroot/bin/fish*
file build-hnp/sysroot/bin/fish*
✅ 构建验证结果:
- ✅ Fish 可执行文件已安装:
fish(1.5M) - Fish Shell 主程序fish_indent(1.5M) - Fish 脚本格式化工具fish_key_reader(1.5M) - 键盘输入测试工具
- ✅ 文件类型:ELF 64-bit LSB pie executable, ARM aarch64
- ✅ 动态链接:
dynamically linked, interpreter /lib/ld-musl-aarch64.so.1 - ✅ 已剥离符号:
stripped - ✅ 配置文件(位于
build-hnp/sysroot/etc/fish/):config.fish- 系统级配置文件completions/、functions/、conf.d/目录
- ✅ 补全和函数脚本(位于
build-hnp/sysroot/share/fish/):completions/- 大量命令补全脚本(900+ 个文件)functions/- 大量函数脚本(200+ 个文件)config.fish- 默认配置文件
- ✅ 已打包到
base.hnp中
💻 终端中执行的示例命令
🐟 Fish 基本使用
1. 启动 Fish Shell
# 启动 Fish Shell
fish
# 启动 Fish 并执行命令
fish -c "echo 'Hello, Fish!'"
# 启动 Fish 并执行脚本
fish script.fish
# 检查 Fish 版本
fish --version

2. 语法高亮
# Fish 会自动高亮命令
ls -la # 有效命令会高亮
invalid_cmd # 无效命令会以红色显示
# 实时语法检查
echo "Hello" # 正确语法
echo "Hello # 未闭合引号会高亮显示错误

3. 自动补全
# 输入命令时按 Tab 键自动补全
ls Desktop/ # 按 Tab 会显示所有以D开头的命令
# 补全显示描述信息
git comm # 按 Tab 会显示 commit、commit-tree 等选项及其描述
# 多选补全
cd ~/Doc # 按 Tab 会显示 Documents、Downloads 等选项
# 上下文感知补全
git checkout # 按 Tab 会显示分支名和文件名的补全

4. 命令历史
# 增量搜索历史命令
# 输入命令的前几个字符,Fish 会自动显示匹配的历史命令
# 查看命令历史
history
# 搜索历史命令
history | grep git
# 清除历史
history clear
# 历史共享
# Fish 会自动在多个会话间共享命令历史
5. 变量和环境
# 设置变量
set myvar "Hello, World!"
echo $myvar
# 设置全局变量
set -gx MYVAR "Global Variable"
echo $MYVAR
# 设置路径变量
set -gx PATH $PATH /new/path
# 列出所有变量
set
# 删除变量
set -e myvar
# 环境变量
set -gx EDITOR vim
set -gx LANG en_US.UTF-8
6. 函数定义
# 定义函数
function hello
echo "Hello, $argv[1]!"
end
# 调用函数
hello World
# 带参数的函数
function greet
set name $argv[1]
echo "Hello, $name!"
end
greet Alice
# 保存函数到文件
# 函数保存在 ~/.config/fish/functions/ 目录
function hello --description "Say hello"
echo "Hello, World!"
end
# 使用 funcsave 保存函数
funcsave hello

7. 条件语句和循环
# if 语句
if test $status -eq 0
echo "Command succeeded"
else
echo "Command failed"
end
# for 循环
for i in 1 2 3 4 5
echo "Number: $i"
end
# while 循环
set i 1
while test $i -le 5
echo "Number: $i"
set i (math $i + 1)
end
# switch 语句
switch $argv[1]
case start
echo "Starting..."
case stop
echo "Stopping..."
case '*'
echo "Unknown command"
end
8. 字符串操作
# 字符串连接
set str1 "Hello"
set str2 "World"
set combined "$str1, $str2!"
echo $combined
# 字符串长度
set str "Hello"
echo (string length $str)
# 字符串替换
set str "Hello World"
echo (string replace "World" "Fish" $str)
# 字符串分割
set str "a,b,c"
string split "," $str
# 字符串匹配
if string match -q "Hello*" "Hello World"
echo "Matched!"
end
9. 文件操作
# 读取文件内容
cat file.txt
# 写入文件
echo "Hello" > file.txt
# 追加到文件
echo "World" >> file.txt
# 检查文件是否存在
if test -f file.txt
echo "File exists"
end
# 检查目录是否存在
if test -d /path/to/dir
echo "Directory exists"
end
# 列出文件
ls -la
# 查找文件
find . -name "*.fish"
# 读取文件行
for line in (cat file.txt)
echo $line
end
10. 进程和作业管理
# 后台运行进程
sleep 10 &
# 列出作业
jobs
# 前台作业
fg %1
# 后台作业
bg %1
# 杀死作业
kill %1
# 等待进程
wait %1
# 检查进程状态
ps aux | grep fish
11. Fish 配置
# 编辑配置文件
fish_config
# 手动编辑配置
vim ~/.config/fish/config.fish
# 配置示例
# ~/.config/fish/config.fish
# 设置提示符
function fish_prompt
echo -n (prompt_pwd) "> "
end
# 设置别名
alias ll "ls -la"
alias la "ls -A"
alias l "ls -CF"
# 设置路径
set -gx PATH $PATH ~/bin
# 设置环境变量
set -gx EDITOR vim
set -gx LANG en_US.UTF-8
# 加载函数
source ~/.config/fish/functions/myfunction.fish
12. Fish 辅助工具
# fish_indent - 格式化 Fish 脚本
fish_indent script.fish
# 格式化并输出
fish_indent script.fish > formatted.fish
# fish_key_reader - 测试键盘输入
fish_key_reader
# 测试特定键
fish_key_reader -c "Ctrl+C"
# fish_config - 交互式配置(需要 Web 浏览器)
fish_config
# 启动配置服务器
fish_config start
13. 实际应用示例
# 开发环境设置
# ~/.config/fish/config.fish
function dev_setup
set -gx GOPATH ~/go
set -gx PATH $PATH $GOPATH/bin
set -gx EDITOR vim
end
# 项目导航
function proj
switch $argv[1]
case work
cd ~/projects/work
case personal
cd ~/projects/personal
case '*'
echo "Unknown project"
end
end
# Git 快捷命令
function gst
git status
end
function gco
git checkout $argv[1]
end
function gcm
git commit -m $argv[1]
end
# 快速查找
function ff
find . -name "*$argv[1]*"
end
# 快速 grep
function gg
grep -r "$argv[1]" .
end
# 系统信息
function sysinfo
echo "OS: "(uname -s)
echo "Kernel: "(uname -r)
echo "Architecture: "(uname -m)
echo "Uptime: "(uptime)
end
14. 功能验证脚本
#!/bin/bash
# Fish Shell 工具验证脚本
FISH_BIN="build-hnp/sysroot/bin"
FISH_SHARE="build-hnp/sysroot/share/fish"
echo "=== Fish Shell 工具验证 ==="
# 检查可执行文件
echo ""
echo "=== 可执行文件验证 ==="
for exe in fish fish_indent fish_key_reader; do
if [ -f "$FISH_BIN/$exe" ]; then
echo "✓ $exe: 存在"
file "$FISH_BIN/$exe"
echo " 文件大小: $(ls -lh "$FISH_BIN/$exe" | awk '{print $5}')"
echo " 架构信息: $(file "$FISH_BIN/$exe" | grep -o "ARM aarch64")"
echo " 链接类型: $(file "$FISH_BIN/$exe" | grep -o "dynamically linked\|statically linked")"
else
echo "✗ $exe: 缺失"
fi
done
# 检查配置文件
echo ""
echo "=== 配置文件验证 ==="
if [ -f "build-hnp/sysroot/etc/fish/config.fish" ]; then
echo "✓ config.fish: 存在"
else
echo "✗ config.fish: 缺失"
fi
# 检查补全脚本
echo ""
echo "=== 补全脚本验证 ==="
if [ -d "$FISH_SHARE/completions" ]; then
count=$(find "$FISH_SHARE/completions" -type f | wc -l)
echo "✓ completions: 存在 ($count 个文件)"
else
echo "✗ completions: 缺失"
fi
# 检查函数脚本
echo ""
echo "=== 函数脚本验证 ==="
if [ -d "$FISH_SHARE/functions" ]; then
count=$(find "$FISH_SHARE/functions" -type f | wc -l)
echo "✓ functions: 存在 ($count 个文件)"
else
echo "✗ functions: 缺失"
fi
# 测试基本功能(需要在目标设备上运行)
echo ""
echo "=== 功能测试(需要在目标设备上运行)==="
echo "启动 Fish Shell:"
echo " $FISH_BIN/fish"
echo ""
echo "检查版本:"
echo " $FISH_BIN/fish --version"
echo ""
echo "格式化脚本:"
echo " $FISH_BIN/fish_indent script.fish"
echo ""
echo "测试键盘输入:"
echo " $FISH_BIN/fish_key_reader"
🐛 构建过程遇到的问题及解决方法
❌ 问题 1:PCRE2 32位库缺失
- 🔍 症状:链接错误,找不到
pcre2_*_32函数 - 🔎 原因:Fish 需要 PCRE2 32位版本处理宽字符,但默认只构建了8位版本
- ✅ 解决方法:
- 修改 PCRE2 的构建配置,启用32位版本:
--enable-pcre2-32 - 更新 fish 的 Makefile,使用
libpcre2-32.so而不是libpcre2-8.so - 在链接标志中添加
-lpcre2-32 - 位置:
build-hnp/pcre2/Makefile:8、build-hnp/fish/Makefile:15
- 修改 PCRE2 的构建配置,启用32位版本:
❌ 问题 2:终端属性设置问题(TCSANOW)
- 🔍 症状:在 OHOS 环境下终端属性立即生效可能导致异常
- 🔎 原因:
TCSANOW在 OHOS 环境下行为不一致 - ✅ 解决方法:
- 应用补丁将所有
TCSANOW,替换为TCSADRAIN, - 涉及文件:
src/reader.cpp、src/fish_key_reader.cpp、src/builtins/fg.cpp - 位置:
build-hnp/fish/Makefile:7-9
- 应用补丁将所有
❌ 问题 3:GitHub 下载失败
- 🔍 症状:无法从 GitHub 下载源码包
- 🔎 原因:网络问题或 GitHub 访问受限
- ✅ 解决方法:
- 手动下载源码包放置到
build-hnp/fish/download/目录 - 使用代理或镜像站点下载
- 通用下载规则支持
curl兜底 - 位置:
build-hnp/fish/Makefile:3-5
- 手动下载源码包放置到
❌ 问题 4:CMake 配置失败
- 🔍 症状:CMake 无法找到 PCRE2 或 Curses 库
- 🔎 原因:CMake 自动查找失败或路径不正确
- ✅ 解决方法:
- 禁用 Curses 自动查找:
-DCMAKE_DISABLE_FIND_PACKAGE_Curses=ON - 手动指定库路径和头文件路径
- 确保 PCRE2 32位库和 Curses 库已正确安装到
sysroot - 位置:
build-hnp/fish/Makefile:15
- 禁用 Curses 自动查找:
❌ 问题 5:HAP 安装失败
- 🔍 症状:HAP 安装时失败,提示特殊文件名问题
- 🔎 原因:Fish 补全目录中存在名为
..fish的特殊文件 - ✅ 解决方法:
- 在安装后删除该文件:
rm -fv build$(PREFIX)/share/fish/completions/..fish - 位置:
build-hnp/fish/Makefile:13
- 在安装后删除该文件:
❌ 问题 6:getpwuid 问题
- 🔍 症状:编译或运行时
getpwuid相关错误 - 🔎 原因:OHOS 环境下
getpwuid行为不一致 - ✅ 解决方法:
- 应用
0002-fix-getpwuid.diff修复相关问题 - 位置:
build-hnp/fish/Makefile:11
- 应用
❌ 问题 7:交叉编译配置失败
- 🔍 症状:CMake 无法检测到交叉编译器
- 🔎 原因:环境变量未正确设置或工具链路径不正确
- ✅ 解决方法:
- 确保通过
create-hnp.sh触发构建以获得完整环境变量 - 检查
OHOS_SDK_HOME是否正确设置 - 手动指定编译器路径:
-DCMAKE_C_COMPILER、-DCMAKE_CXX_COMPILER - 位置:
build-hnp/fish/Makefile:15
- 确保通过
❌ 问题 8:架构不支持
- 🔍 症状:
Unsupported OHOS_ARCH= - 🔎 原因:传入的架构不在支持列表中
- ✅ 解决方法:
- 确保传入支持架构(
aarch64或x86_64) - 位置:
build-hnp/Makefile:1-49
- 确保传入支持架构(
🔄 重建与扩展
-
🔧 重建单包:
make -C build-hnp rebuild-fish # 触发子包重新编译并刷新 .stamp -
🧹 清理:
make -C build-hnp clean # 清理 sysroot、所有 .stamp 和 PKGS_MARKER -
📦 扩展:Fish Shell 是现代化命令行交互的核心工具,提供了出色的用户体验
-
🔄 自动重建机制:
- 修改
PKGS后,check-pkgs会自动检测变化并触发重新构建 - 新增外部 HNP 包到
external-hnp目录后,会自动合并到base.hnp
- 修改
💡 实践建议
- 🔧 构建配置:确保 PCRE2 32位版本已构建,fish 需要它来处理宽字符
- 🚀 性能优化:Fish Shell 提供了出色的交互体验和性能
- 📦 依赖管理:确保 PCRE2 和 ncurses 正确安装
- 🔗 配置定制:根据需求定制
~/.config/fish/config.fish配置文件 - 🌐 用户体验:Fish Shell 开箱即用,无需复杂配置即可获得优秀体验
📝 结论与建议
- ✅ 本次已在 aarch64 环境下完成 Fish Shell 3.7.1 的交叉编译与打包,可执行文件和大量补全/函数脚本已安装到
sysroot并纳入 HNP 包。 - 💡 为保证构建稳定:
- 使用 CMake 构建系统,配置灵活
- 应用补丁修复 OHOS 环境下的终端属性和 getpwuid 问题
- 确保 PCRE2 32位版本已构建(fish 需要它处理宽字符)
- 确保通过
create-hnp.sh触发构建以获得完整环境变量 - 利用
check-pkgs机制自动检测包列表变化,无需手动清理 - Fish Shell 为命令行交互提供了现代化的用户体验
- 常见陷阱包括 PCRE2 32位库缺失、终端属性设置问题、CMake 配置失败;当前已通过补丁和配置参数处理
- 建议充分利用 Fish Shell 的自动补全、语法高亮和智能提示功能
- CMake 交叉编译参数与依赖注入清晰,补丁适配终端行为差异,保证在 OHOS 环境的稳定性
- 产物完整,上层可直接使用
sysroot/bin/fish,并复用丰富的补全与函数脚本提升交互体验
📚 以上为 Fish Shell 构建的深度解读与实践记录。
更多推荐




所有评论(0)