Administrator
Published on 2026-03-14 / 4 Visits
0
0

Linux 中的 .run 文件是什么?和 .sh 脚本有什么区别


在 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 压缩包

执行时:

  1. 先执行脚本

  2. 解压内部文件

  3. 执行安装程序


二、什么是 .sh 文件

.sh 文件是最常见的 Shell 脚本文件,用于编写 Linux 自动化脚本。

常见用途:

  • 运维脚本

  • 自动化部署

  • 系统管理

  • 批量操作

例如:

#!/bin/bash

echo "Hello Linux"
date
whoami

执行方式:

bash script.sh

或者

chmod +x script.sh
./script.sh

.sh 的本质就是 纯文本 Shell 脚本


三、.run.sh 的核心区别

对比项

.run

.sh

文件类型

安装程序/可执行包

Shell脚本

本质

Shell脚本或二进制封装

纯Shell脚本

是否可包含程序

可以

不可以

是否自解压

常见

不支持

使用场景

软件安装包

自动化脚本

是否统一标准

没有统一标准

标准Shell脚本

简单理解:

.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__

解释:

部分

作用

shebang

指定解释器

shell脚本

控制安装逻辑

archive

压缩程序数据

执行 .run 时:

1️⃣ 执行脚本
2️⃣ 解压 archive
3️⃣ 执行安装


五、.run 文件简单示例

下面演示如何自己制作一个 .run 安装程序。

5.1、 创建程序目录

mkdir myapp
echo "Hello MyApp" > myapp/app.txt

5.2、 打包程序

tar czf myapp.tar.gz myapp

5.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.run

5.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.deb

  • app2-2.3.1.tar.gz

目标是做到:

  1. 一次执行 ./install.run

  2. 自动安装 .deb

  3. 自动解压并部署 tar.gz

  4. 自动创建软链接

  5. 自动输出安装结果


示例目录结构

先准备一个目录,例如:

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

它会自动:

  1. 解压内嵌的压缩包到 /tmp

  2. 执行里面真正的 install.run

  3. 完成两个软件包的安装

6.2、 驱动程序安装

例如:

NVIDIA-Linux-x86_64.run

6.3、 商业软件发布

很多商业软件使用 .run

  • JetBrains Toolbox

  • 游戏服务器

  • 数据库工具

原因:

  • 不依赖系统包管理器

  • 可跨发行版


七、.run 与 Linux 软件包的区别

类型

示例

特点

run

xxx.run

通用安装包

rpm

xxx.rpm

RedHat 系

deb

xxx.deb

Debian 系

tar.gz

xxx.tar.gz

源码包

.run 的优势:

  • 不依赖系统发行版

  • 自带安装逻辑

  • 可跨 Linux 发行版


八、如何查看 .run 文件内容

方法一:

sh file.run --extract

方法二:

tail
tail -n +100 file.run

方法三:

strings file.run

九、安全注意事项

运行 .run 文件前建议:

1 查看文件内容

less file.run

2 检查来源

确保来自官方。

3 不要随便 root 运行

除非必须。


十、总结

.run 文件并不是一种新的脚本语言,本质上是一种 自解压可执行安装包格式

核心区别总结:

类型

本质

.sh

Shell脚本

.run

可执行安装程序

理解一句话:

.sh 是脚本
.run安装包

在 Linux 软件分发领域,.run 由于 跨发行版、使用简单、无需依赖包管理器,仍然被大量软件厂商使用。



Comment