在 Linux 运维或软件安装过程中,经常会遇到两种常见的脚本文件:
.sh文件.run文件
很多初学者会疑惑:
.run文件到底是什么?它和.sh有什么区别?是不是一种新的脚本语言?
实际上,.run 并不是一种新的脚本语言,本质上仍然是 Shell 脚本或可执行程序的封装形式。
一、什么是 .run 文件
.run 文件是 Linux 中常见的一种 可执行安装包格式,通常用于:
软件安装程序
驱动程序安装
二进制程序分发
自解压安装包
常见的软件安装包:
NVIDIA 驱动
VMware Tools
某些商业软件
例如:
NVIDIA-Linux-x86_64-550.54.run
VMware-Workstation-Full-17.0.0.run运行方式通常是:
chmod +x file.run
./file.run本质原理
.run 文件通常是以下结构之一:
1️⃣ Shell脚本 + 压缩数据
#!/bin/bash
安装逻辑
自解压逻辑
程序数据2️⃣ 自解压安装包
脚本头
tar.gz 压缩包执行时:
先执行脚本
解压内部文件
执行安装程序
二、什么是 .sh 文件
.sh 文件是最常见的 Shell 脚本文件,用于编写 Linux 自动化脚本。
常见用途:
运维脚本
自动化部署
系统管理
批量操作
例如:
#!/bin/bash
echo "Hello Linux"
date
whoami执行方式:
bash script.sh或者
chmod +x script.sh
./script.sh.sh 的本质就是 纯文本 Shell 脚本。
三、.run 与 .sh 的核心区别
简单理解:
.sh是脚本.run是 安装程序封装
四、.run 文件内部结构解析
我们可以用 head 查看 .run 文件头部:
head -n 20 example.run示例:
#!/bin/sh
# Self-extracting archive
echo "Extracting files..."
ARCHIVE=`awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' $0`
tail -n+$ARCHIVE $0 | tar xz
exit 0
__ARCHIVE_BELOW__
解释:
执行 .run 时:
1️⃣ 执行脚本
2️⃣ 解压 archive
3️⃣ 执行安装
五、.run 文件简单示例
下面演示如何自己制作一个 .run 安装程序。
5.1、 创建程序目录
mkdir myapp
echo "Hello MyApp" > myapp/app.txt5.2、 打包程序
tar czf myapp.tar.gz myapp5.3、 创建 run 安装脚本
#!/bin/bash
echo "Installing MyApp..."
ARCHIVE_LINE=$(awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' $0)
tail -n+$ARCHIVE_LINE $0 | tar xz
echo "Install complete"
exit 0
__ARCHIVE_BELOW__
5.4、 合并脚本和数据
cat install.sh myapp.tar.gz > myapp.run5.5、 添加执行权限
chmod +x myapp.run运行:
./myapp.run输出:
Installing MyApp...
Install complete程序会自动解压。

六、.run 常见应用场景
6.1、软件安装包
方式 1:目录式 run
例如:
VMware-Workstation-17.run运行:
sudo ./VMware-Workstation-17.run假设你有两个离线软件包,需要通过一个 install.run 一起安装:
app1_1.0.0_amd64.debapp2-2.3.1.tar.gz
目标是做到:
一次执行
./install.run自动安装
.deb自动解压并部署
tar.gz自动创建软链接
自动输出安装结果
示例目录结构
先准备一个目录,例如:
offline_installer/
├── install.run
├── packages/
│ ├── app1_1.0.0_amd64.deb
│ └── app2-2.3.1.tar.gz
└── scripts/
└── post_install.sh说明:
packages/用来放离线安装包install.run是总安装入口post_install.sh是可选的安装后处理脚本
install.run 样例:
#!/bin/bash
set -e
########################################
# Offline Installer Demo
# 安装两个离线软件包:
# 1) app1_1.0.0_amd64.deb
# 2) app2-2.3.1.tar.gz
########################################
APP1_DEB="app1_1.0.0_amd64.deb"
APP2_TAR="app2-2.3.1.tar.gz"
INSTALL_BASE="/opt"
APP2_DIR="${INSTALL_BASE}/app2"
APP2_LINK="/usr/local/bin/app2"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PKG_DIR="${SCRIPT_DIR}/packages"
SCRIPT_POST="${SCRIPT_DIR}/scripts/post_install.sh"
LOG_FILE="/tmp/offline_install_$(date +%Y%m%d_%H%M%S).log"
log() {
echo "[INFO] $1" | tee -a "$LOG_FILE"
}
warn() {
echo "[WARN] $1" | tee -a "$LOG_FILE"
}
error() {
echo "[ERROR] $1" | tee -a "$LOG_FILE"
exit 1
}
check_root() {
if [ "$EUID" -ne 0 ]; then
error "请使用 root 或 sudo 执行该安装程序。"
fi
}
check_files() {
[ -f "${PKG_DIR}/${APP1_DEB}" ] || error "未找到 ${PKG_DIR}/${APP1_DEB}"
[ -f "${PKG_DIR}/${APP2_TAR}" ] || error "未找到 ${PKG_DIR}/${APP2_TAR}"
}
install_deb() {
log "开始安装 DEB 包:${APP1_DEB}"
if command -v dpkg >/dev/null 2>&1; then
dpkg -i "${PKG_DIR}/${APP1_DEB}" 2>&1 | tee -a "$LOG_FILE" || true
else
error "系统未找到 dpkg,无法安装 deb 包。"
fi
# 如果系统有 apt-get,可以尝试修复依赖
if command -v apt-get >/dev/null 2>&1; then
log "尝试修复依赖(若依赖已离线准备好或系统已有则可成功)"
apt-get install -f -y 2>&1 | tee -a "$LOG_FILE" || warn "依赖修复未成功,请确认依赖是否已提前准备。"
else
warn "未找到 apt-get,跳过依赖修复。"
fi
log "DEB 包安装步骤完成。"
}
install_tar() {
log "开始安装 TAR.GZ 包:${APP2_TAR}"
mkdir -p "$APP2_DIR"
# 先清理旧版本(可按需删除)
rm -rf "${APP2_DIR:?}/"*
tar -xzf "${PKG_DIR}/${APP2_TAR}" -C "$APP2_DIR" --strip-components=1 2>&1 | tee -a "$LOG_FILE"
# 假设 tar.gz 解压后主程序叫 app2
if [ -f "${APP2_DIR}/app2" ]; then
chmod +x "${APP2_DIR}/app2"
ln -sf "${APP2_DIR}/app2" "$APP2_LINK"
log "已创建软链接:${APP2_LINK} -> ${APP2_DIR}/app2"
else
warn "未找到 ${APP2_DIR}/app2,请根据实际程序名修改脚本。"
fi
log "TAR.GZ 包安装完成。"
}
run_post_install() {
if [ -f "$SCRIPT_POST" ]; then
log "执行安装后脚本:${SCRIPT_POST}"
chmod +x "$SCRIPT_POST"
"$SCRIPT_POST" 2>&1 | tee -a "$LOG_FILE"
else
warn "未找到 post_install.sh,跳过安装后处理。"
fi
}
show_summary() {
echo
echo "======================================"
echo "安装完成"
echo "日志文件:$LOG_FILE"
echo "DEB包:${APP1_DEB}"
echo "TAR包:${APP2_TAR}"
echo "APP2安装目录:${APP2_DIR}"
echo "APP2启动命令:${APP2_LINK}"
echo "======================================"
echo
}
main() {
log "离线安装开始"
check_root
check_files
install_deb
install_tar
run_post_install
show_summary
}
main "$@"post_install.sh 样例
如果你想在安装完成后做一些初始化动作,比如创建目录、配置文件、日志目录,可以配一个脚本:
#!/bin/bash
mkdir -p /etc/app2
mkdir -p /var/log/app2
if [ ! -f /etc/app2/app2.conf ]; then
cat > /etc/app2/app2.conf <<EOF
# app2 default config
port=8080
log_level=info
EOF
fi
echo "post install finished."赋予执行权限
给安装脚本加执行权限:
chmod +x install.run
chmod +x scripts/post_install.sh然后执行:
sudo ./install.run如果 .deb 也有离线依赖,怎么一起装
这是实际环境里最常见的问题。
因为 .deb 很可能依赖其他 .deb,所以更稳妥的做法是:
把所有依赖包都放到 packages/ 下,然后统一安装。
例如目录改成:
offline_installer/
├── install.run
├── packages/
│ ├── liba.deb
│ ├── libb.deb
│ ├── app1_1.0.0_amd64.deb
│ └── app2-2.3.1.tar.gz安装逻辑可以改成:
install_all_debs() {
log "开始安装目录中的全部 DEB 包"
find "${PKG_DIR}" -maxdepth 1 -name "*.deb" | sort | while read -r deb_file; do
log "安装: ${deb_file}"
dpkg -i "$deb_file" 2>&1 | tee -a "$LOG_FILE" || true
done
if command -v apt-get >/dev/null 2>&1; then
apt-get install -f -y 2>&1 | tee -a "$LOG_FILE" || warn "依赖修复未成功"
fi
log "全部 DEB 包安装完成"
}然后把主流程里的 install_deb 换成 install_all_debs。
如果想做成“单文件 run 包” 通常有两种方式:
就是现在这种:
一个
install.run外加
packages/目录
优点:
简单
易维护
调试方便
缺点:
不是单文件
方式 2:单文件自解压 run
把脚本和安装包打到一个文件里,运行时先解压再安装。
这种更像真正的软件发行包。
单文件 .run 自解压样例
先写一个 installer_stub.sh:
#!/bin/bash
set -e
TMP_DIR="/tmp/offline_bundle_$$"
ARCHIVE_LINE=$(awk '/^__ARCHIVE_BELOW__/ {print NR + 1; exit 0; }' "$0")
mkdir -p "$TMP_DIR"
tail -n +"$ARCHIVE_LINE" "$0" | tar -xz -C "$TMP_DIR"
chmod +x "$TMP_DIR/install.run"
exec "$TMP_DIR/install.run"
exit 0
__ARCHIVE_BELOW__然后把你的安装目录打包:
tar -czf payload.tar.gz packages scripts install.run
cat installer_stub.sh payload.tar.gz > offline_bundle.run
chmod +x offline_bundle.run运行时:
sudo ./offline_bundle.run它会自动:
解压内嵌的压缩包到
/tmp执行里面真正的
install.run完成两个软件包的安装
6.2、 驱动程序安装
例如:
NVIDIA-Linux-x86_64.run6.3、 商业软件发布
很多商业软件使用 .run:
JetBrains Toolbox
游戏服务器
数据库工具
原因:
不依赖系统包管理器
可跨发行版
七、.run 与 Linux 软件包的区别
.run 的优势:
不依赖系统发行版
自带安装逻辑
可跨 Linux 发行版
八、如何查看 .run 文件内容
方法一:
sh file.run --extract方法二:
tailtail -n +100 file.run方法三:
strings file.run
九、安全注意事项
运行 .run 文件前建议:
1 查看文件内容
less file.run2 检查来源
确保来自官方。
3 不要随便 root 运行
除非必须。
十、总结
.run 文件并不是一种新的脚本语言,本质上是一种 自解压可执行安装包格式。
核心区别总结:
理解一句话:
.sh是脚本.run是 安装包
在 Linux 软件分发领域,.run 由于 跨发行版、使用简单、无需依赖包管理器,仍然被大量软件厂商使用。