目录

项目简介

sd (search & displace) 是一个直观的查找与替换命令行工具,使用 Rust 编写。它提供了比传统 sed 更简洁、更易用的语法,特别适合日常文本处理任务。

主要特性

  • 简洁的正则表达式语法:使用 JavaScript 和 Python 风格的正则表达式,无需学习 sed 的复杂语法
  • 字符串字面量模式:非正则表达式的查找替换,无需转义特殊字符
  • 易于读写:查找和替换表达式分开,避免未闭合和转义的斜杠问题
  • 智能默认设置:遵循常识,针对典型日常使用场景优化
  • 高性能:比 sed 快 2-12 倍(根据基准测试)

与 sed 的对比

功能sdsed
替换所有出现sd before aftersed s/before/after/g
换行符替换sd '\n' ','sed ':a;N;$!ba;s/\n/,/g'
原地修改文件sd before after file.txtsed -i -e 's/before/after/g' file.txt

sd 工具介绍

安装方式

从 Cargo 安装
cargo install sd
从包管理器安装

支持多种包管理器,详见 Packaging status

基本使用

命令行选项
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             显示帮助信息
使用示例
  1. 字符串字面量模式(使用 -F 标志):
echo 'lots((([]))) of special chars' | sd -F '((([])))' ''
# 输出: lots of special chars
  1. 基本正则表达式
# 去除尾随空白
echo 'lorem ipsum 23   ' | sd '\s+$' ''
# 输出: lorem ipsum 23
  1. 捕获组和替换
echo 'cargo +nightly toolchain' | sd '(\w+)\s+\+(\w+)\s+(\w+)' 'cmd: $1, channel: $2, args: $3'
# 输出: cmd: cargo, channel: nightly, args: toolchain
  1. 文件原地修改
sd 'old_text' 'new_text' file.txt
  1. 预览模式(不实际修改):
sd -p 'old_text' 'new_text' file.txt
  1. 多文件处理
sd 'old_text' 'new_text' file1.txt file2.txt file3.txt
  1. 管道输入
cat file.txt | sd 'old' 'new'

性能优势

根据基准测试,sd 在处理大文件时表现优异:

  • 简单替换(~1.5GB JSON):比 sed 快约 2.35 倍
  • 正则替换(~55MB JSON):比 sed 快约 11.93 倍

开始之前先给大家贴一张图片

image-20251115111246669

适配 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 文件:

{
"type":"hnp-config",
"name":"sd",
"version":"1.0.1",
"install":{}
}

3. Rust 项目特点

  • 语言:Rust
  • 构建工具:Cargo
  • 目标平台:需要交叉编译到 aarch64-linux-ohos
  • 依赖:标准库 + 第三方 crate(regex, rayon, memmap2 等)

构建脚本实现

完整的 build_ohos.sh

#!/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"

构建流程说明

  1. 环境准备:设置安装路径和 PREFIX
  2. 清理构建:清理之前的构建产物
  3. 工具链选择:使用 stable Rust 工具链
  4. 交叉编译配置:创建 .cargo/config.toml 配置交叉编译
  5. Target 安装:安装 aarch64-unknown-linux-musl target
  6. 构建项目:使用 cargo build --release --target
  7. 安装文件:安装二进制文件和手册页
  8. 打包:生成 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

# 错误的方式
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 特性声明:

修改前:

#![feature(try_blocks)]
mod cli;

修改后:

// Removed nightly feature: #![feature(try_blocks)]
// This feature is not actually used in the code
mod cli;

构建脚本配置:

# 使用 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 块:

修改前:

pub(crate) unsafe fn make_mmap(path: &PathBuf) -> Result<Mmap> {
  Ok(Mmap::map(&File::open(path)?)?)
  }

修改后:

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 配置文件,指定正确的链接器和链接参数:

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
问题分析

构建脚本在编译失败时,安装目录没有被创建,导致后续步骤失败。

解决方案

在构建前创建所有必要的目录:

# 创建安装目录(确保目录存在)
mkdir -p ${SD_INSTALL_HNP_PATH}/bin
mkdir -p ${SD_INSTALL_HNP_PATH}/share/man/man1

并添加错误处理:

# 检查二进制文件是否存在
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)

验证命令

# 检查文件是否存在
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 # 手册页

使用示例

基本替换

# 简单字符串替换
echo "Hello World" | sd "World" "HarmonyOS"
# 输出: Hello HarmonyOS
# 文件原地修改
sd "old_text" "new_text" file.txt
# 多文件处理
sd "old" "new" file1.txt file2.txt file3.txt

正则表达式

# 去除尾随空白
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

字符串字面量模式

# 使用 -F 标志,将模式视为字面字符串
echo 'lots((([]))) of special chars' | sd -F '((([])))' ''
# 输出: lots of special chars
# 替换包含特殊字符的字符串
echo "path/to/file" | sd -F "/" "-"
# 输出: path-to-file

预览模式

# 预览替换结果,不实际修改文件
sd -p "old" "new" file.txt
# 从管道预览
cat file.txt | sd -p "old" "new"

高级用法

# 限制替换次数
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

实际应用场景

  1. 批量文件替换
# 替换项目中所有文件中的文本
find . -type f -name "*.rs" -exec sd "old_api" "new_api" {} \;
  1. 配置文件修改
# 修改配置文件中的值
sd 'port\s*=\s*\d+' 'port = 8080' config.toml
  1. 代码重构
# 重命名函数调用
sd 'old_function\(' 'new_function(' src/**/*.rs
  1. 数据清洗
# 清理 CSV 文件中的多余空白
sd '\s+,' ',' data.csv

Rust 交叉编译要点

1. Target 选择

HarmonyOS 使用 musl libc,因此选择 aarch64-unknown-linux-musl target:

rustup target add aarch64-unknown-linux-musl

2. 链接器配置

通过 .cargo/config.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. 环境变量

设置交叉编译相关的环境变量:

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. 构建命令

cargo build --release --target aarch64-unknown-linux-musl

总结

适配要点

  1. 识别项目类型:Rust 项目使用 cargo 而非 make
  2. 移除 nightly 特性:检查并移除未使用的 nightly 特性声明
  3. 修复 unsafe 警告:在 unsafe fn 中添加显式 unsafe
  4. 配置交叉编译:创建 .cargo/config.toml 配置链接器
  5. 错误处理完善:添加完善的错误检查和错误处理机制

技术亮点

  • Rust 交叉编译:成功配置 Rust 项目交叉编译到 HarmonyOS
  • 链接器集成:正确集成 HarmonyOS SDK 的链接器
  • 代码兼容性:修复 Rust 2024 版本的兼容性问题
  • 构建自动化:完整的构建脚本,支持自动化构建和打包

适用场景

sd 工具特别适用于:

  • 文本处理:批量查找和替换文本
  • 代码重构:批量修改代码中的函数名、变量名等
  • 配置文件管理:批量修改配置文件
  • 数据处理:清洗和转换数据文件
  • 日志分析:处理和转换日志文件

性能优势

根据基准测试,sd 在处理大文件时比 sed 快 2-12 倍,特别适合:

  • 处理大型 JSON/XML 文件
  • 批量处理多个文件
  • 需要高性能的文本替换场景

参考资源


所以我们今天可以说,Rust开发的命令行工具,也是可以适配鸿蒙的。