1

我正在尝试制作我的脚本来检查我在 github 中的 repo 是否有更新,然后获取更新并用新代码替换旧代码并运行“不是旧代码”的新代码。我想出了这个,但它在完成后更新

self_update() {
    cd $(dirname $0)
    git fetch > a.txt 1> /dev/null 2> /dev/null
    git reset --hard >> a.txt 1> /dev/null 2> /dev/null
    git pull >> a.txt 1> /dev/null 2> /dev/null
    rm a.txt
    chmod +x "$(basename $0)"
    cd -
}
self_update
echo “some code”

编辑:我在stackoverflow找到了下面的代码,它更新了我的脚本。然而,它进入了一个循环,从不运行新的或旧的代码,不知道为什么。

#!/bin/bash

SCRIPT=$(readlink -f "$0")
SCRIPTPATH=$(dirname "$SCRIPT")
SCRIPTNAME="$0"
ARGS="( $@ )"
BRANCH="master"

self_update() {
    cd $SCRIPTPATH
    git fetch

    [ -n $(git diff --name-only origin/$BRANCH | grep $SCRIPTNAME) ] && {
        echo "Found a new version of me, updating myself..."
        git pull --force
        git checkout $BRANCH
        git pull --force
        echo "Running the new version..."
        exec "$SCRIPTNAME" "${ARGS[@]}"

        # Now exit this old instance
        exit 1
    }
    echo "Already the latest version."
}
self_update
echo “some code”

重复输出:

 Found a new version of me, updating myself...
 HEAD is now at 5dd5111 Update tool
 Already up to date
 Already on ‘master’
 Your branch is up to date with origin/master

它不会停止打印输出,直到我 CTRL-C 输出:执行:bash -x /opt/script/firstScript -h

++ readlink -f /opt/script/firstScript
+ SCRIPT=/opt/script/firstScript  
++ dirname /opt/script/firstScript
+ SCRIPTPATH=/opt/script                                
+ SCRIPTNAME=/opt/script/firstScript
+ ARGS='( -h )'
+ BRANCH=master
+ self_update      
+ cd /opt/script
+ git fetch                                                
++ git diff --name-only origin/master
++ grep /opt/script/firstScript
+ '[' -n ']'                                               
+ echo 'Found a new version of me, updating myself...'
Found a new version of me, updating myself...
+ git pull --force 
Already up to date.  
+ git checkout master
Already on 'master'
Your branch is up to date with 'origin/master'.
+ git pull --force
Already up to date.
+ echo 'Running the new version...'
Running the new version...
+ exec bash -x /opt/script/firstScript '( -h )'
++ readlink -f /opt/script/firstScript
+ SCRIPT=/opt/script/firstScript
++ dirname /opt/script/firstScript
+ SCRIPTPATH=/opt/script
+ SCRIPTNAME=/opt/script/firstScript
+ ARGS='( ( -h ) )'
+ BRANCH=master
+ self_update
+ cd /opt/script
+ git fetch
++ git diff --name-only origin/master
++ grep /opt/script/firstScript
+ '[' -n ']'
+ echo 'Found a new version of me, updating myself...'
Found a new version of me, updating myself...
+ git pull --force
Already up to date.
+ git checkout master
Already on 'master'
Your branch is up to date with 'origin/master'.
+ git pull --force
Already up to date.
+ echo 'Running the new version...'
Running the new version...
+ exec bash -x /opt/script/firstScript '( ( -h ) )'
++ readlink -f /opt/script/firstScript
+ SCRIPT=/opt/script/firstScript
++ dirname /opt/script/firstScript
+ SCRIPTPATH=/opt/script
+ SCRIPTNAME=/opt/script/firstScript
+ ARGS='( ( ( -h ) ) )'
+ BRANCH=master
+ self_update
+ cd /opt/script
+ git fetch
++ git diff --name-only origin/master
++ grep /opt/script/firstScript
+ '[' -n ']'
+ echo 'Found a new version of me, updating myself...'
Found a new version of me, updating myself...
+ git pull --force
Already up to date.
+ git checkout master
Already on 'master'
Your branch is up to date with 'origin/master'.
+ git pull --force
^C

输出:执行:bash /opt/script/firstScript -h

Found a new version of me, updating myself...
Already up to date.
Your branch is up to date with 'origin/master'.
Already up to date.
Running the new version...
Found a new version of me, updating myself...
Already up to date.
Your branch is up to date with 'origin/master'.
Already up to date.
Running the new version...
Found a new version of me, updating myself...
Already up to date.
Your branch is up to date with 'origin/master'.
Already up to date.
Running the new version...
Found a new version of me, updating myself...
Already up to date.
Your branch is up to date with 'origin/master'.
Already up to date.
Running the new version...
Found a new version of me, updating myself...
Already up to date.
Your branch is up to date with 'origin/master'.
Already up to date.
Running the new version...
Found a new version of me, updating myself...
Already up to date.
Your branch is up to date with 'origin/master'.
Already up to date.
Running the new version...
Found a new version of me, updating myself...
Already up to date.
Your branch is up to date with 'origin/master'.
Already up to date.
Running the new version...
Found a new version of me, updating myself...
^C
4

5 回答 5

0

脚本无法自行更新可能有多种原因。暂时搁置“为什么”,考虑使用基于环境变量的“双重调用”守卫。它将防止重复尝试更新。

self_update() {
    [ "$UPDATE_GUARD" ] && return
    export UPDATE_GUARD=YES

    cd $SCRIPTPATH
    git fetch

    [ -n $(git diff --name-only origin/$BRANCH | grep $SCRIPTNAME) ] && {
        echo "Found a new version of me, updating myself..."
        git pull --force
        git checkout $BRANCH
        git pull --force
        echo "Running the new version..."
        exec "$SCRIPTNAME" "${ARGS[@]}"

        # Now exit this old instance
        exit 1
    }
    echo "Already the latest version."
}

此外,self_update如果它可能会影响运行脚本,请考虑将其更改为返回到原始 cwd。

于 2020-01-14T19:30:16.527 回答
0

阅读 bash -x 输出后,我可以给你重写脚本

#!/bin/bash
                                               # Here I remark changes

SCRIPT="$(readlink -f "$0")"
SCRIPTFILE="$(basename "$SCRIPT")"             # get name of the file (not full path)
SCRIPTPATH="$(dirname "$SCRIPT")"
SCRIPTNAME="$0"
ARGS=( "$@" )                                  # fixed to make array of args (see below)
BRANCH="master"

self_update() {
    cd "$SCRIPTPATH"
    git fetch

                                               # in the next line
                                               # 1. added double-quotes (see below)
                                               # 2. removed grep expression so
                                               # git-diff will check only script
                                               # file
    [ -n "$(git diff --name-only "origin/$BRANCH" "$SCRIPTFILE")" ] && {
        echo "Found a new version of me, updating myself..."
        git pull --force
        git checkout "$BRANCH"
        git pull --force
        echo "Running the new version..."
        cd -                                   # return to original working dir
        exec "$SCRIPTNAME" "${ARGS[@]}"

        # Now exit this old instance
        exit 1
    }
    echo "Already the latest version."
}
self_update
echo “some code”

低于 1ARGS="( $@ )"绝对应该是,否则在使用参数而不是ARGS=( "$@" )执行更新脚本之后(通常,它使用连接在单个字符串中的所有参数执行,即您将其运行为,并且在更新后它运行为'( -h )'-h/opt/script/firstScript -a -b -c/opt/script/firstScript '( -a -b -c )'

2以下。双引号是必需的$(...),否则[ -n用作]输入参数并返回true,因为它不为空(而空输出git-diff|grep在参数列表中被忽略[ -n)(这是循环原因

于 2020-01-15T12:35:05.693 回答
0

我怀疑您没有为当前分支设置上游跟踪。然后git fetch什么都不做。尝试

git fetch origin master

相反(假设这是您想要的上游和分支)。

您似乎也不了解exec您找到的代码中的重要性。这将用更新的版本替换当前正在执行的脚本,并从头开始运行它。没有办法

update_code
echo "some stuff"

echo "some stuff"在更新自身后立即。相反,它将再次执行自身,希望这次使用更新版本的代码。

然而,

[ -n $(git diff --name-only origin/$BRANCH | grep $SCRIPTNAME) ]

是一个非常迂回且可能很脆弱的结构。您在询问是否grep返回任何(非空)输出......但显然,grep它本身就是一个检查是否有任何匹配的工具。此外,在这里使用很脆弱——如果使用包含的SCRIPTNAME路径调用脚本,但只会输出相对路径(在这种情况下,如果是 Git 工作目录),因此将失败。(在您的成绩单中,您会看到——这正是这个错误。)/home/you/work/script/update_self_testSCRIPTNAMEgit diffscript/update_self_test/home/you/workgrepbash -xgrep /opt/script/firstScript

既然你已经在文件的目录中,我会提倡

git diff --name-only origin/master "$(basename "$SCRIPTNAME")"

如果文件没有更改,则不会打印任何内容,如果有,则显示该单个文件的文件名。不幸的是,这并没有设置它的退出代码来指示成功或失败(我想这里很难定义,尽管传统的约定是常规diff命令在有差异时报告非零退出代码)但我们可以使用

git diff --name-only origin/master "$(basename "$SCRIPTNAME")" | grep -q . &&

对于整个条件。(还要注意何时在 shell 变量周围加上引号?

最后,您自己的尝试还有另一个问题。除了失败之外exec,您还有不明确的重定向。您尝试将内容发送到文件a.txt,但您也尝试将相同的内容发送到/dev/null. 它是哪一个?下定决心。

物有所值,

echo testing >a.txt 1>/dev/null

发送testing/dev/null; 它首先将标准输出重定向到a.txt,然后更新重定向,因此a.txt如果它尚不存在,您只需在其中创建一个空文件。

最终,您可能还应该为私有变量切换为小写;但我看到您只是从另一个答案中复制了错误的约定。

于 2020-01-15T04:53:26.413 回答
0

基于这里的所有答案,以及我自己的一些要求:

  • 超时 git(!)
  • 静音,无输出
  • 存储本地更改
  • 递归循环陷阱
  • 始终使用 origin/main
  • 使用 git diff 退出代码而不是输出

我做了以下版本:

#!/bin/bash
                                               # Here I remark changes
#
SCRIPT="$(readlink -f "$0")"
SCRIPTFILE="$(basename "$SCRIPT")"             # get name of the file (not full path)
SCRIPTPATH="$(dirname "$SCRIPT")"
SCRIPTNAME="$0"
ARGS=( "$@" )                                  # fixed to make array of args (see below)

self_update() {
    [ "$UPDATE_GUARD" ] && return
    export UPDATE_GUARD=YES
    
    cd "$SCRIPTPATH"
    timeout 1s git fetch --quiet

                                               # in the next line
                                               # 1. added double-quotes (see below)
                                               # 2. removed grep expression so
                                               # git-diff will check only script
                                               # file

    timeout 1s git diff --quiet --exit-code "origin/main" "$SCRIPTFILE"
    [ $? -eq 1 ] && {
        #echo "Found a new version of me, updating myself..."
        if [ -n "$(git status --porcelain)" ];  # opposite is -z
        then 
            git stash push -m 'local changes stashed before self update' --quiet
        fi
        git pull --force --quiet
        git checkout main --quiet
        git pull --force --quiet
        #echo "Running the new version..."
        cd - > /dev/null                        # return to original working dir
        exec "$SCRIPTNAME" "${ARGS[@]}"

        # Now exit this old instance
        exit 1
    }
    #echo "Already the latest version."
}
self_update
echo "some code2"
于 2021-07-20T19:59:36.283 回答
-1

您的脚本启动了“self_update”bash 函数,它自己调用了exec "$SCRIPTNAME" "${ARGS[@]}". 但如果我读得好,$SCRIPTNAME是你的剧本。

您的脚本将继续递归调用自身。这就是你循环的原因。

您是否考虑过使用诸如cron之类的东西来运行您的脚本,而不是让它自己调用?

编辑:如果您在其中进行了本地更改,那么测试中的 git 命令git diff --name-only origin/$BRANCH也会响应包含脚本文件的行,并永远循环。

于 2020-01-14T13:07:40.337 回答