目录
项目简介
sd (s earch & d isplace) 是一个直观的查找与替换命令行工具,使用 Rust 编写。它提供了比传统 sed 更简洁、更易用的语法,特别适合日常文本处理任务。
主要特性
- 简洁的正则表达式语法 :使用 JavaScript 和 Python 风格的正则表达式,无需学习
sed的复杂语法 - 字符串字面量模式:非正则表达式的查找替换,无需转义特殊字符
- 易于读写:查找和替换表达式分开,避免未闭合和转义的斜杠问题
- 智能默认设置:遵循常识,针对典型日常使用场景优化
- 高性能 :比
sed快 2-12 倍(根据基准测试)
与 sed 的对比
| 功能 | sd | sed |
|---|---|---|
| 替换所有出现 | sd before after |
sed s/before/after/g |
| 换行符替换 | sd '\n' ',' |
sed ':a;N;$!ba;s/\n/,/g' |
| 原地修改文件 | sd before after file.txt |
sed -i -e 's/before/after/g' file.txt |
sd 工具介绍
安装方式
从 Cargo 安装
bash
cargo install sd
从包管理器安装
支持多种包管理器,详见 Packaging status。
基本使用
命令行选项
bash
sd [OPTIONS] <find> <replace_with> [files]...
Arguments:
<find> 要查找的模式(正则表达式或字符串)
<replace_with> 替换为的内容
[files]... 要处理的文件(可选,默认从 stdin 读取)
Options:
-F, --fixed-strings 将模式视为字面字符串而非正则表达式
-p, --preview 预览模式,不实际修改文件
-f, --flags <FLAGS> 正则表达式标志(如 i=忽略大小写)
-r, --replacements <N> 限制替换次数
-h, --help 显示帮助信息
使用示例
- 字符串字面量模式 (使用
-F标志):
bash
echo 'lots((([]))) of special chars' | sd -F '((([])))' ''
# 输出: lots of special chars
- 基本正则表达式:
bash
# 去除尾随空白
echo 'lorem ipsum 23 ' | sd '\s+$' ''
# 输出: lorem ipsum 23
- 捕获组和替换:
bash
echo 'cargo +nightly toolchain' | sd '(\w+)\s+\+(\w+)\s+(\w+)' 'cmd: $1, channel: $2, args: $3'
# 输出: cmd: cargo, channel: nightly, args: toolchain
- 文件原地修改:
bash
sd 'old_text' 'new_text' file.txt
- 预览模式(不实际修改):
bash
sd -p 'old_text' 'new_text' file.txt
- 多文件处理:
bash
sd 'old_text' 'new_text' file1.txt file2.txt file3.txt
- 管道输入:
bash
cat file.txt | sd 'old' 'new'
性能优势
根据基准测试,sd 在处理大文件时表现优异:
- 简单替换 (~1.5GB JSON):比
sed快约 2.35 倍 - 正则替换 (~55MB JSON):比
sed快约 11.93 倍
开始之前先给大家贴一张图片

适配 HarmonyOS 的准备工作
1. 项目结构
code/sd/
├── build_ohos.sh # HarmonyOS 构建脚本
├── hnp.json # HNP 包配置文件
├── Cargo.toml # Rust 项目配置
├── src/ # 源代码目录
│ ├── main.rs # 主程序
│ ├── cli.rs # 命令行参数解析
│ ├── input.rs # 输入处理
│ └── replacer.rs # 替换逻辑
├── gen/ # 生成的文档
│ └── sd.1 # 手册页
└── .cargo/ # Cargo 配置(构建时生成)
└── config.toml # 交叉编译配置
2. HNP 配置文件
创建 hnp.json 文件:
json
{
"type":"hnp-config",
"name":"sd",
"version":"1.0.1",
"install":{}
}
3. Rust 项目特点
- 语言:Rust
- 构建工具:Cargo
- 目标平台 :需要交叉编译到
aarch64-linux-ohos - 依赖:标准库 + 第三方 crate(regex, rayon, memmap2 等)
构建脚本实现
完整的 build_ohos.sh
bash
#!/bin/bash
# ============================================================================
# build_ohos.sh - sd 工具 HarmonyOS 构建脚本
# ============================================================================
# sd 是一个 Rust 项目,使用 cargo 构建
# ============================================================================
export SD_INSTALL_HNP_PATH=${HNP_PUBLIC_PATH}/sd.org/sd_1.0.1
sys_prefix=${PREFIX}
export PREFIX=${SD_INSTALL_HNP_PATH}
echo "${PREFIX}"
# 创建安装目录(确保目录存在)
mkdir -p ${SD_INSTALL_HNP_PATH}/bin
mkdir -p ${SD_INSTALL_HNP_PATH}/share/man/man1
# 清理之前的构建产物
cargo clean || true
# 使用 stable 工具链
# 注意:源代码中的 try_blocks 特性声明已被移除,因为实际代码中并未使用
export RUSTUP_TOOLCHAIN=stable
# 配置 Rust 交叉编译环境
# HarmonyOS 使用 musl libc,目标平台为 aarch64-linux-ohos
# 由于 Rust 可能没有标准的 ohos target,我们使用通用的 linux-musl target
export TARGET_CC="${CC}"
export TARGET_CXX="${CXX}"
export TARGET_AR="${AR}"
export TARGET_LD="${LD}"
export TARGET_CFLAGS="${CFLAGS}"
export TARGET_LDFLAGS="${LDFLAGS}"
# 创建 .cargo/config.toml 配置交叉编译
mkdir -p .cargo
cat > .cargo/config.toml << CARGO_CONFIG
[target.aarch64-unknown-linux-musl]
linker = "${CC}"
rustflags = [
"-C", "link-arg=--target=aarch64-linux-ohos",
"-C", "link-arg=--sysroot=${SYSROOT}",
"-C", "link-arg=--ld-path=${LD}",
"-C", "link-arg=-fuse-ld=lld",
]
CARGO_CONFIG
# 尝试使用 aarch64-unknown-linux-musl target(最接近 HarmonyOS)
RUST_TARGET="aarch64-unknown-linux-musl"
# 检查 target 是否已安装
if ! rustup target list --installed | grep -q "^${RUST_TARGET}$"; then
echo "Installing Rust target: ${RUST_TARGET}"
rustup target add ${RUST_TARGET} || {
echo "Warning: Failed to install ${RUST_TARGET}, trying default build"
RUST_TARGET=""
}
fi
# 构建项目
if [ -n "${RUST_TARGET}" ]; then
echo "Building for target: ${RUST_TARGET}"
cargo build --release --target ${RUST_TARGET} || {
echo "Error: cargo build failed"
exit 1
}
BINARY_PATH="target/${RUST_TARGET}/release/sd"
else
echo "Building for default target"
cargo build --release || {
echo "Error: cargo build failed"
exit 1
}
BINARY_PATH="target/release/sd"
fi
# 检查二进制文件是否存在
if [ ! -f "${BINARY_PATH}" ]; then
echo "Error: Binary not found at ${BINARY_PATH}"
exit 1
fi
# 安装二进制文件
install -m 0755 ${BINARY_PATH} ${SD_INSTALL_HNP_PATH}/bin/sd || {
echo "Error: Failed to install binary"
exit 1
}
# 安装手册页(如果存在)
if [ -f "gen/sd.1" ]; then
gzip -c gen/sd.1 > gen/sd.1.gz 2>/dev/null || true
install -m 0644 gen/sd.1.gz ${SD_INSTALL_HNP_PATH}/share/man/man1/ 2>/dev/null || true
rm -f gen/sd.1.gz
fi
# 复制 HNP 配置文件
cp hnp.json ${SD_INSTALL_HNP_PATH}/ || {
echo "Error: failed to copy hnp.json"
exit 1
}
# 打包
if [ -d "${SD_INSTALL_HNP_PATH}" ]; then
pushd ${SD_INSTALL_HNP_PATH}/../ > /dev/null
${HNP_TOOL} pack -i ${SD_INSTALL_HNP_PATH} -o ${ARCHIVE_PATH}/ || {
echo "Error: HNP pack failed"
popd > /dev/null
export PREFIX=${sys_prefix}
exit 1
}
tar -zvcf ${ARCHIVE_PATH}/ohos_sd_1.0.1.tar.gz sd_1.0.1/ || {
echo "Error: tar failed"
popd > /dev/null
export PREFIX=${sys_prefix}
exit 1
}
popd > /dev/null
else
echo "Error: Installation directory does not exist: ${SD_INSTALL_HNP_PATH}"
export PREFIX=${sys_prefix}
exit 1
fi
# 恢复 PREFIX
export PREFIX=${sys_prefix}
echo "Build completed successfully!"
echo "Output files:"
echo " - ${ARCHIVE_PATH}/sd.hnp"
echo " - ${ARCHIVE_PATH}/ohos_sd_1.0.1.tar.gz"
构建流程说明
- 环境准备:设置安装路径和 PREFIX
- 清理构建:清理之前的构建产物
- 工具链选择:使用 stable Rust 工具链
- 交叉编译配置 :创建
.cargo/config.toml配置交叉编译 - Target 安装 :安装
aarch64-unknown-linux-musltarget - 构建项目 :使用
cargo build --release --target - 安装文件:安装二进制文件和手册页
- 打包:生成 HNP 包和 tar.gz 压缩包
遇到的问题与解决方案
问题一:Makefile 不存在
错误现象
make: *** No rule to make target `clean'. Stop.
make: *** No targets specified and no makefile found. Stop.
问题分析
sd 是一个 Rust 项目,使用 cargo 构建,而不是传统的 make。构建脚本错误地使用了 make 命令。
解决方案
将构建脚本改为使用 cargo:
bash
# 错误的方式
make clean
make VERBOSE=1
make install
# 正确的方式
cargo clean
cargo build --release --target aarch64-unknown-linux-musl
问题二:Nightly 特性依赖
错误现象
error[E0554]: `#![feature]` may not be used on the stable release channel
--> src/main.rs:1:1
|
1 | #![feature(try_blocks)]
| ^^^^^^^^^^^^^^^^^^^^^^^
问题分析
源代码中声明了 #![feature(try_blocks)],这是一个 nightly 特性,但实际代码中并未使用该特性。使用 stable 工具链时会出现错误。
解决方案
移除未使用的 nightly 特性声明:
修改前:
rust
#![feature(try_blocks)]
mod cli;
修改后:
rust
// Removed nightly feature: #![feature(try_blocks)]
// This feature is not actually used in the code
mod cli;
构建脚本配置:
bash
# 使用 stable 工具链
export RUSTUP_TOOLCHAIN=stable
问题三:Unsafe 函数警告
错误现象
warning[E0133]: call to unsafe function `memmap2::Mmap::map` is unsafe and requires unsafe block
--> src/input.rs:37:8
|
37 | Ok(Mmap::map(&File::open(path)?)?)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
问题分析
Rust 2024 版本要求 unsafe fn 中调用 unsafe 函数时必须显式使用 unsafe 块。
解决方案
在 unsafe fn 中添加显式的 unsafe 块:
修改前:
rust
pub(crate) unsafe fn make_mmap(path: &PathBuf) -> Result<Mmap> {
Ok(Mmap::map(&File::open(path)?)?)
}
修改后:
rust
pub(crate) unsafe fn make_mmap(path: &PathBuf) -> Result<Mmap> {
unsafe {
Ok(Mmap::map(&File::open(path)?)?)
}
}
问题四:链接器配置错误
错误现象
error: linking with `cc` failed: exit status: 1
= note: ld: unknown option: --as-needed
clang: error: linker command failed with exit code 1
问题分析
Rust 的 aarch64-unknown-linux-musl target 默认使用系统的 cc 链接器,但需要配置为使用 HarmonyOS SDK 的链接器(ld.lld)。
解决方案
创建 .cargo/config.toml 配置文件,指定正确的链接器和链接参数:
bash
mkdir -p .cargo
cat > .cargo/config.toml << CARGO_CONFIG
[target.aarch64-unknown-linux-musl]
linker = "${CC}"
rustflags = [
"-C", "link-arg=--target=aarch64-linux-ohos",
"-C", "link-arg=--sysroot=${SYSROOT}",
"-C", "link-arg=--ld-path=${LD}",
"-C", "link-arg=-fuse-ld=lld",
]
CARGO_CONFIG
关键配置说明:
linker = "${CC}":使用 HarmonyOS SDK 的 clang 作为链接器前端--target=aarch64-linux-ohos:指定目标平台--sysroot=${SYSROOT}:指定系统根目录--ld-path=${LD}:指定链接器路径(ld.lld)-fuse-ld=lld:使用 LLVM 的 lld 链接器
问题五:目录不存在错误
错误现象
cp: directory /Users/jianguo/HarmonyOSPC/build/data/service/hnp/sd.org/sd_1.0.1 does not exist
pushd: /Users/jianguo/HarmonyOSPC/build/data/service/hnp/sd.org/sd_1.0.1/../: No such file or directory
问题分析
构建脚本在编译失败时,安装目录没有被创建,导致后续步骤失败。
解决方案
在构建前创建所有必要的目录:
bash
# 创建安装目录(确保目录存在)
mkdir -p ${SD_INSTALL_HNP_PATH}/bin
mkdir -p ${SD_INSTALL_HNP_PATH}/share/man/man1
并添加错误处理:
bash
# 检查二进制文件是否存在
if [ ! -f "${BINARY_PATH}" ]; then
echo "Error: Binary not found at ${BINARY_PATH}"
exit 1
fi
# 打包前检查目录是否存在
if [ -d "${SD_INSTALL_HNP_PATH}" ]; then
# ... 打包操作 ...
else
echo "Error: Installation directory does not exist"
exit 1
fi
构建结果验证
构建输出
成功构建后,会在 output/ 目录下生成以下文件:
sd.hnp- HarmonyOS Native Package 格式包(约 914KB)ohos_sd_1.0.1.tar.gz- tar.gz 压缩包(约 917KB)
验证命令
bash
# 检查文件是否存在
ls -lh output/sd.hnp output/ohos_sd_1.0.1.tar.gz
# 检查 HNP 文件格式
file output/sd.hnp
# 查看 tar.gz 内容
tar -tzf output/ohos_sd_1.0.1.tar.gz
安装目录结构
sd_1.0.1/
├── bin/
│ └── sd # 可执行文件
├── hnp.json # HNP 配置文件
└── share/
└── man/
└── man1/
└── sd.1.gz # 手册页
使用示例
基本替换
bash
# 简单字符串替换
echo "Hello World" | sd "World" "HarmonyOS"
# 输出: Hello HarmonyOS
# 文件原地修改
sd "old_text" "new_text" file.txt
# 多文件处理
sd "old" "new" file1.txt file2.txt file3.txt
正则表达式
bash
# 去除尾随空白
echo "lorem ipsum 23 " | sd '\s+$' ''
# 输出: lorem ipsum 23
# 捕获组替换
echo "cargo +nightly toolchain" | sd '(\w+)\s+\+(\w+)\s+(\w+)' 'cmd: $1, channel: $2, args: $3'
# 输出: cmd: cargo, channel: nightly, args: toolchain
# 数字替换
echo "price: 100" | sd '(\d+)' '$$$1'
# 输出: price: $100
字符串字面量模式
bash
# 使用 -F 标志,将模式视为字面字符串
echo 'lots((([]))) of special chars' | sd -F '((([])))' ''
# 输出: lots of special chars
# 替换包含特殊字符的字符串
echo "path/to/file" | sd -F "/" "-"
# 输出: path-to-file
预览模式
bash
# 预览替换结果,不实际修改文件
sd -p "old" "new" file.txt
# 从管道预览
cat file.txt | sd -p "old" "new"
高级用法
bash
# 限制替换次数
echo "foo foo foo" | sd -r 2 "foo" "bar"
# 输出: bar bar foo
# 使用正则表达式标志(忽略大小写)
echo "Hello HELLO hello" | sd -f i "hello" "hi"
# 输出: hi hi hi
# 处理包含换行符的文本
echo -e "line1\nline2\nline3" | sd '\n' ','
# 输出: line1,line2,line3
实际应用场景
- 批量文件替换:
bash
# 替换项目中所有文件中的文本
find . -type f -name "*.rs" -exec sd "old_api" "new_api" {} \;
- 配置文件修改:
bash
# 修改配置文件中的值
sd 'port\s*=\s*\d+' 'port = 8080' config.toml
- 代码重构:
bash
# 重命名函数调用
sd 'old_function\(' 'new_function(' src/**/*.rs
- 数据清洗:
bash
# 清理 CSV 文件中的多余空白
sd '\s+,' ',' data.csv
Rust 交叉编译要点
1. Target 选择
HarmonyOS 使用 musl libc,因此选择 aarch64-unknown-linux-musl target:
bash
rustup target add aarch64-unknown-linux-musl
2. 链接器配置
通过 .cargo/config.toml 配置交叉编译:
toml
[target.aarch64-unknown-linux-musl]
linker = "clang"
rustflags = [
"-C", "link-arg=--target=aarch64-linux-ohos",
"-C", "link-arg=--sysroot=${SYSROOT}",
"-C", "link-arg=--ld-path=${LD}",
"-C", "link-arg=-fuse-ld=lld",
]
3. 环境变量
设置交叉编译相关的环境变量:
bash
export TARGET_CC="${CC}"
export TARGET_CXX="${CXX}"
export TARGET_AR="${AR}"
export TARGET_LD="${LD}"
export TARGET_CFLAGS="${CFLAGS}"
export TARGET_LDFLAGS="${LDFLAGS}"
4. 构建命令
bash
cargo build --release --target aarch64-unknown-linux-musl
总结
适配要点
- 识别项目类型 :Rust 项目使用
cargo而非make - 移除 nightly 特性:检查并移除未使用的 nightly 特性声明
- 修复 unsafe 警告 :在
unsafe fn中添加显式unsafe块 - 配置交叉编译 :创建
.cargo/config.toml配置链接器 - 错误处理完善:添加完善的错误检查和错误处理机制
技术亮点
- Rust 交叉编译:成功配置 Rust 项目交叉编译到 HarmonyOS
- 链接器集成:正确集成 HarmonyOS SDK 的链接器
- 代码兼容性:修复 Rust 2024 版本的兼容性问题
- 构建自动化:完整的构建脚本,支持自动化构建和打包
适用场景
sd 工具特别适用于:
- 文本处理:批量查找和替换文本
- 代码重构:批量修改代码中的函数名、变量名等
- 配置文件管理:批量修改配置文件
- 数据处理:清洗和转换数据文件
- 日志分析:处理和转换日志文件
性能优势
根据基准测试,sd 在处理大文件时比 sed 快 2-12 倍,特别适合:
- 处理大型 JSON/XML 文件
- 批量处理多个文件
- 需要高性能的文本替换场景
参考资源
所以我们今天可以说,Rust开发的命令行工具,也是可以适配鸿蒙的。