如何写一个脚本自动更新脚本本身

wen 实用脚本 1

本文目录导读:

如何写一个脚本自动更新脚本本身

  1. 方法一:最简单的版本检查(推荐入门)
  2. 方法二:带备份和回滚机制
  3. 方法三:Python脚本自动更新
  4. 方法四:使用Git进行更新
  5. 使用建议
  6. 最简单的方案(适合个人使用)

我来给你介绍几种自动更新脚本的方法,从简单到复杂:

最简单的版本检查(推荐入门)

#!/bin/bash
# 简单的自我更新脚本
# 脚本当前版本
VERSION="1.0.0"
# 远程版本文件地址
REMOTE_VERSION_URL="https://example.com/version.txt"
# 脚本远程地址
SCRIPT_URL="https://example.com/script.sh"
# 脚本保存路径
SCRIPT_PATH="$0"
echo "当前版本: $VERSION"
# 检查远程版本
REMOTE_VERSION=$(curl -s "$REMOTE_VERSION_URL")
if [ "$REMOTE_VERSION" != "$VERSION" ]; then
    echo "发现新版本: $REMOTE_VERSION,开始更新..."
    # 下载新版本到临时文件
    curl -s "$SCRIPT_URL" -o "$SCRIPT_PATH.tmp"
    # 检查下载是否成功
    if [ $? -eq 0 ] && [ -s "$SCRIPT_PATH.tmp" ]; then
        # 替换原脚本
        mv "$SCRIPT_PATH.tmp" "$SCRIPT_PATH"
        chmod +x "$SCRIPT_PATH"
        echo "更新完成!"
        # 重新执行新脚本
        exec "$SCRIPT_PATH"
    else
        echo "下载失败,保持原版本"
        rm -f "$SCRIPT_PATH.tmp"
    fi
else
    echo "已经是最新版本"
fi

带备份和回滚机制

#!/bin/bash
# 带备份和回滚的更新脚本
VERSION="2.0.0"
REMOTE_INFO="https://example.com/update_info.json"
SCRIPT_PATH="$0"
BACKUP_PATH="${SCRIPT_PATH}.backup"
echo "检查更新中..."
# 获取远程版本信息
if command -v jq &> /dev/null; then
    REMOTE_INFO_DATA=$(curl -s "$REMOTE_INFO")
    REMOTE_VERSION=$(echo "$REMOTE_INFO_DATA" | jq -r '.version')
    DOWNLOAD_URL=$(echo "$REMOTE_INFO_DATA" | jq -r '.download_url')
    CHANGELOG=$(echo "$REMOTE_INFO_DATA" | jq -r '.changelog')
else
    # 如果没有 jq,使用 grep/sed 解析
    REMOTE_VERSION=$(curl -s "$REMOTE_INFO" | grep -o '"version":"[^"]*"' | cut -d'"' -f4)
    DOWNLOAD_URL=$(curl -s "$REMOTE_INFO" | grep -o '"url":"[^"]*"' | cut -d'"' -f4)
    CHANGELOG="有新版本可用"
fi
echo "当前版本: $VERSION"
echo "远程版本: $REMOTE_VERSION"
# 版本比较函数
version_compare() {
    [ "$1" = "$2" ] && return 0
    [ "$1" = "$(echo -e "$1\n$2" | sort -V | head -n1)" ] && return 2 || return 1
}
version_compare "$REMOTE_VERSION" "$VERSION"
RESULT=$?
if [ $RESULT -eq 1 ]; then
    echo "发现新版本!更新内容:"
    echo "$CHANGELOG"
    # 询问用户是否更新
    read -p "是否更新?(y/N): " CONFIRM
    if [[ "$CONFIRM" != "y" && "$CONFIRM" != "Y" ]]; then
        echo "取消更新"
        exit 0
    fi
    # 备份当前脚本
    cp "$SCRIPT_PATH" "$BACKUP_PATH"
    echo "已备份当前版本到 $BACKUP_PATH"
    # 下载新版本
    echo "下载新版本..."
    curl -sL "$DOWNLOAD_URL" -o "$SCRIPT_PATH.tmp"
    # 验证下载的脚本
    if [ -s "$SCRIPT_PATH.tmp" ] && file "$SCRIPT_PATH.tmp" | grep -q "script"; then
        # 替换脚本
        mv "$SCRIPT_PATH.tmp" "$SCRIPT_PATH"
        chmod +x "$SCRIPT_PATH"
        echo "更新成功!"
        # 询问是否立即执行新版本
        read -p "是否立即执行新版本?(Y/n): " EXECUTE_NEW
        if [[ "$EXECUTE_NEW" != "n" && "$EXECUTE_NEW" != "N" ]]; then
            echo "正在启动新版本..."
            exec "$SCRIPT_PATH" "$@"
        fi
    else
        echo "下载的脚本无效,回滚到备份版本"
        mv "$BACKUP_PATH" "$SCRIPT_PATH"
        chmod +x "$SCRIPT_PATH"
    fi
else
    echo "已经是最新版本"
fi

Python脚本自动更新

#!/usr/bin/env python3
"""
Python脚本自动更新示例
"""
import sys
import os
import json
import urllib.request
import hashlib
import tempfile
import shutil
import subprocess
from pathlib import Path
class AutoUpdater:
    def __init__(self, version="1.0.0"):
        self.version = version
        self.update_url = "https://example.com/update_info.json"
        self.script_path = Path(sys.argv[0]).resolve()
    def check_update(self):
        """检查是否有更新"""
        try:
            with urllib.request.urlopen(self.update_url, timeout=10) as response:
                data = json.loads(response.read())
                remote_version = data['version']
                download_url = data['download_url']
                remote_md5 = data.get('md5', '')
                changelog = data.get('changelog', '')
                if self.version != remote_version:
                    print(f"发现新版本: {remote_version}")
                    if changelog:
                        print(f"更新说明: {changelog}")
                    return True, download_url, remote_md5
                else:
                    return False, None, None
        except Exception as e:
            print(f"检查更新失败: {e}")
            return False, None, None
    def download_script(self, url):
        """下载新版本脚本"""
        try:
            print("下载新版本...")
            with urllib.request.urlopen(url, timeout=30) as response:
                data = response.read()
                # 保存到临时文件
                temp_file = tempfile.NamedTemporaryFile(
                    mode='wb', 
                    delete=False, 
                    suffix='.py'
                )
                temp_file.write(data)
                temp_file.close()
                return temp_file.name
        except Exception as e:
            print(f"下载失败: {e}")
            return None
    def validate_script(self, file_path, expected_md5=None):
        """验证下载的脚本"""
        if not os.path.exists(file_path):
            return False
        if expected_md5:
            with open(file_path, 'rb') as f:
                md5 = hashlib.md5(f.read()).hexdigest()
                if md5 != expected_md5:
                    print("MD5校验失败")
                    return False
        # 检查文件是否包含有效的Python代码
        try:
            with open(file_path, 'r', encoding='utf-8') as f:
                compile(f.read(), file_path, 'exec')
            return True
        except:
            return False
    def backup_current(self):
        """备份当前脚本"""
        backup_path = Path(f"{self.script_path}.backup")
        shutil.copy2(self.script_path, backup_path)
        print(f"备份到: {backup_path}")
        return backup_path
    def update(self):
        """执行更新"""
        has_update, download_url, md5 = self.check_update()
        if not has_update:
            print("已经是最新版本")
            return True
        # 备份
        backup_path = self.backup_current()
        # 下载新版本
        new_file = self.download_script(download_url)
        if not new_file:
            print("更新失败")
            return False
        # 验证新文件
        if not self.validate_script(new_file, md5):
            print("新版本验证失败,回滚中...")
            self.rollback(backup_path)
            return False
        # 替换文件
        try:
            os.replace(new_file, self.script_path)
            os.chmod(self.script_path, 0o755)  # 添加执行权限
            print("更新成功!")
            # 询问是否重启
            choice = input("是否重新运行新版本?(y/n): ")
            if choice.lower() == 'y':
                print("正在启动新版本...")
                subprocess.run([sys.executable, str(self.script_path)])
            return True
        except Exception as e:
            print(f"更新失败: {e}")
            self.rollback(backup_path)
            return False
    def rollback(self, backup_path):
        """回滚到备份版本"""
        if backup_path.exists():
            shutil.copy2(backup_path, self.script_path)
            print("已回滚到备份版本")
    def run(self):
        """主函数"""
        # 检查更新
        if "--check-update" in sys.argv:
            self.check_update()
            return
        # 执行更新(默认自动检查更新)
        print(f"脚本版本: {self.version}")
        choice = input("是否检查更新?(y/n): ")
        if choice.lower() == 'y':
            self.update()
        # 这里是你的主要功能代码
        print("执行主要功能...")
        # 你的其他代码...
if __name__ == "__main__":
    updater = AutoUpdater(version="1.0.0")
    updater.run()

使用Git进行更新

#!/bin/bash
# 基于Git的自动更新脚本
VERSION_FILE=".version"
REPO_URL="https://github.com/yourname/yourscript.git"
# 获取当前版本
get_current_version() {
    if [ -f "$VERSION_FILE" ]; then
        cat "$VERSION_FILE"
    else
        echo "0.0.0"
    fi
}
# 检查Git仓库更新
check_git_update() {
    echo "检查Git仓库更新..."
    # 初始化Git仓库(如果不存在)
    if [ ! -d ".git" ]; then
        git init
        git remote add origin "$REPO_URL"
        git fetch origin 2>/dev/null
        git checkout -f origin/main 2>/dev/null
        return 1
    fi
    # 获取远程更新
    git remote update 2>/dev/null
    # 比较本地和远程
    LOCAL=$(git rev-parse @ 2>/dev/null)
    REMOTE=$(git rev-parse @{u} 2>/dev/null)
    if [ "$LOCAL" != "$REMOTE" ]; then
        return 0  # 有更新
    else
        return 1  # 没有更新
    fi
}
# 执行Git更新
git_update() {
    echo "发现新版本,开始更新..."
    # 保存当前工作目录
    CUR_DIR=$(pwd)
    # 备份当前版本
    cp -r "$CUR_DIR" "$CUR_DIR.backup"
    # 拉取更新
    git pull origin main 2>/dev/null
    if [ $? -eq 0 ]; then
        # 设置新版本号
        echo "$(date +%Y%m%d.%H%M%S)" > "$VERSION_FILE"
        echo "更新成功!"
        # 询问是否重启
        read -p "是否重新运行?(y/n): " RESTART
        if [ "$RESTART" = "y" ]; then
            exec ./$(basename "$0")
        fi
    else
        echo "更新失败,回滚中..."
        rm -rf "$CUR_DIR"
        mv "$CUR_DIR.backup" "$CUR_DIR"
    fi
}
# 主程序
echo "当前版本: $(get_current_version)"
if check_git_update; then
    git_update
else
    echo "已经是最新版本"
fi
# 以下是主要功能代码
echo "执行主要功能..."

使用建议

  1. 版本管理:始终保留一个稳定版本号
  2. 备份机制:更新前总是备份当前版本
  3. 验证机制:下载后验证文件完整性
  4. 回滚功能:万一更新失败能回退
  5. 安全检查:确保来源可信,可使用MD5/SHA校验
  6. 用户确认:重要更新前询问用户

最简单的方案(适合个人使用)

#!/bin/bash
# 最简单的一行更新命令
# 放在脚本开头
curl -s https://example.com/updater.sh | bash

根据你的需求选择合适的方案,如果是生产环境建议使用带备份和回滚的方法。

抱歉,评论功能暂时关闭!