[英]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.