[英]Self updating bash script from github
我试图让我的脚本检查我在 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
脚本无法自行更新的原因可能有多种。 暂时搁置“为什么”,考虑使用基于环境变量的“双重调用”保护。 它将防止重复尝试更新。
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,如果它可能对运行脚本产生影响。
我怀疑您没有为当前分支设置上游跟踪。 然后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
这里是脆-如果脚本是用类似路径调用/home/you/work/script/update_self_test
这就是SCRIPTNAME
包含但git diff
只会输出相对路径( script/update_self_test
在这种情况下,如果/home/you/work
是 Git 工作目录),因此grep
将失败。 (在您的bash -x
成绩单中,您会看到grep /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
尚不存在,您只需在a.txt
创建一个空文件即可。
最终,您可能还应该将私有变量切换为小写; 但我看到您只是从另一个答案中复制了错误的约定。
阅读 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”
低于 1 . ARGS="( $@ )"
肯定应该是ARGS=( "$@" )
,否则在更新脚本执行后使用'( -h )'
参数而不是-h
(一般来说,它是在所有参数连接在单个字符串,即您将其作为/opt/script/firstScript -a -b -c
运行,并在更新后作为/opt/script/firstScript '( -a -b -c )'
以下 2 . $(...)
周围需要双引号,否则[ -n
使用]
作为输入参数并返回 true 因为它不为空(而git-diff|grep
的空输出在[ -n
参数列表中被忽略) (这是循环原因)
基于这里的所有答案,以及我自己的一些要求:
我做了以下版本:
#!/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"
您的脚本启动 'self_update' bash 函数,它本身调用exec "$SCRIPTNAME" "${ARGS[@]}"
。 但如果我读得好, $SCRIPTNAME
就是你的脚本。
您的脚本将继续递归调用自身。 这就是为什么你在循环。
您是否考虑过使用cron之类的东西运行脚本而不是让它自称?
编辑:还有测试中的 git 命令, git diff --name-only origin/$BRANCH
如果您在其中进行了本地更改,则会以包含脚本文件的行进行响应,并且永远循环。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.