应用程序重定向(Shim)
Window和Linux系统提供的ln/mklink 虽然很好用,但是不够灵活,所以,在很多程序替代时,需要一个同名字的程序作为中转。
参考scoop的实现:
Scoop 不会把每个软件的可执行文件路径直接添加到系统的 PATH 环境变量中,而是采用一种叫 shim(垫片/代理) 的机制:
- 在 Scoop 的 shims 目录 (通常是
~\scoop\shims)中为每个可执行文件创建一个轻量级的代理(.exe或.bat文件)。 - 将这个
shims目录一次性加入系统PATH。 - 当你在命令行输入
git、node、python等命令时,实际运行的是shims目录下的代理程序,它会自动转发到真实程序的位置。
windows下可以使用scoop的shim方案:
https://github.com/ScoopInstaller/Scoop/tree/master/supporting/shims
Linux下没有现成的实现
但是可以使用bash脚本install-shim.sh替代:
bash
#!/bin/bash
# ==========================================
# Shim 安装器
# 功能:将模板脚本中的占位符替换为实际值,并安装到 /usr/bin
# 用法:sudo ./install-shim.sh <目标文件名> <真实程序路径> <基础参数...>
# ==========================================
# 检查是否为 root (因为要写入 /usr/bin)
if [ "$EUID" -ne 0 ]; then
echo "错误: 此脚本必须以 root 权限运行 (sudo)" >&2
echo "用法: sudo $0 <命令名> <真实路径> [基础参数...]"
exit 1
fi
# 检查参数数量
if [ $# -lt 2 ]; then
echo "错误: 参数不足" >&2
echo "用法: sudo $0 <命令名> <真实路径> [基础参数...]"
echo "示例: sudo $0 firefox /opt/firefox/firefox '--no-sandbox'"
exit 1
fi
# --- 解析参数 ---
# $1 是安装后的命令名
CMD_NAME="$1"
# $2 是真实路径
REAL_PATH="$2"
# shift 2 表示移除前两个参数,剩下的都是基础参数
shift 2
BASE_ARGS="$*" # 将剩余所有参数合并为一个字符串
# 安装目标路径
INSTALL_PATH="/usr/bin/$CMD_NAME"
# --- 检查目标程序是否存在 ---
if [ ! -x "$REAL_PATH" ]; then
echo "错误: 真实程序 '$REAL_PATH' 不存在或不可执行" >&2
exit 1
fi
# --- 检查是否已存在同名命令 ---
if command -v "$CMD_NAME" &> /dev/null; then
echo "警告: 命令 '$CMD_NAME' 已存在。"
read -p "是否覆盖现有命令? (y/N): " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "操作已取消。"
exit 1
fi
fi
# --- 定义原始 Shim 模板 ---
# 这里直接使用你提供的原始脚本内容,仅替换 # 为实际值
cat > "/tmp/$CMD_NAME.sh" << EOF
#!/bin/bash
# ==========================================
# Linux Shim 模拟脚本
# 功能:定义固定路径和基础参数,附加用户输入
# ==========================================
# 目标程序的路径 (建议绝对路径)
TARGET_PATH="$REAL_PATH"
# 基础参数 (Shim 固定传递的参数)
BASE_ARGS="$BASE_ARGS"
# --- 2. 核心逻辑 ---
# 检查目标文件是否存在
if [ ! -x "\$TARGET_PATH" ]; then
echo "错误: 无法执行目标程序 '\$TARGET_PATH'" >&2
echo "请确保文件存在且具有可执行权限 (chmod +x)" >&2
exit 1
fi
# --- 3. 执行目标程序 (核心) ---
# "\$@" 代表所有传递给脚本的参数
# exec 替换当前进程镜像,使得目标程序直接接管当前进程 (PID 不变)
# 构建完整的命令行
FULL_COMMAND=("\$TARGET_PATH" \$BASE_ARGS "\$@")
# dump cmd
echo "CMD:" $FULL_COMMAND
# 执行 (exec 会用新程序替换当前 Shell 进程)
exec "\${FULL_COMMAND[@]}"
# 如果 exec 失败,脚本会继续执行到这里
echo "错误: 启动失败" >&2
exit 1
EOF
# --- 安装到 /usr/bin ---
echo "正在安装 '$CMD_NAME' 到 $INSTALL_PATH ..."
# 移动文件并重命名
mv "/tmp/$CMD_NAME.sh" "$INSTALL_PATH"
# 设置可执行权限 (chmod +x)
chmod +x "$INSTALL_PATH"
# --- 完成 ---
echo "安装成功!"
echo "现在你可以直接运行命令: $CMD_NAME"
使用时候:
bash
chmod +x install-shim.sh
sudo ./install-shim.sh mycmd /home/user/app/real_app --debug --config=/tmp/cfg.ini
sudo ./install-shim.sh node `which bun`
sudo ./install-shim.sh npx `which bunx`
使用场景
- bun 替代 node
- bunx 替代 npx
- ...