Самостоятельно обновляемый bash скрипт от github - PullRequest
1 голос
/ 14 января 2020

Я пытаюсь заставить свой скрипт проверить, есть ли обновление из моего репозитория в github, а затем получить обновления и заменить старый код новым и запустить новый код «не старый». Я пришел с этим, но он обновляется после завершения

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 , и он обновляет мой скрипт. Тем не менее, он входит в al oop и никогда не запускает новый или старый код, не знаю почему.

#!/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

Не прекращается печать вывода до i 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 ]

0 голосов
/ 15 января 2020

После прочтения вывода 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) ( Это была l oop причина )

0 голосов
/ 14 января 2020

Может быть несколько причин, по которым скрипт может не обновиться. Оставив на минуту вопрос «почему», рассмотрите возможность использования защиты «двойного вызова» на основе переменной среды. Это предотвратит повторную попытку обновления.

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 на go обратно на исходный cwd, если это может повлиять на запуск скрипта.

0 голосов
/ 15 января 2020

Я подозреваю, что вы не настроили отслеживание восходящего потока для текущей ветви. Тогда 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 . &&

для всего условного. (Обратите внимание также Когда обернуть кавычки вокруг переменной оболочки? )

Наконец, у вашей собственной попытки также есть другая проблема. Помимо сбоя exec, у вас есть неоднозначные перенаправления. Вы пытаетесь отправить материал в файл a.txt, но вы также пытаетесь отправить тот же материал на /dev/null. Что он? Решайтесь.

Для чего это стоит,

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

отправляет testing на /dev/null; сначала он перенаправляет стандартный вывод на a.txt, но затем обновляет перенаправление, так что вы просто создаете пустой файл в a.txt, если он еще не существует.

В конце концов, вам, вероятно, также следует переключиться на нижний регистр для ваших личных переменных; но я вижу, что вы просто скопировали неверное соглашение из другого ответа.

0 голосов
/ 14 января 2020

Ваш скрипт запускает функцию «self_update» bash, которая сама вызывает exec "$SCRIPTNAME" "${ARGS[@]}". Но если я читаю хорошо, $SCRIPTNAME - это ваш сценарий.

Ваш скрипт будет вызывать себя рекурсивно. Вот почему вы зацикливаетесь.

Рассматривали ли вы запуск своего скрипта с чем-то вроде cron вместо того, чтобы вызывать его сам?

Редактировать: Также команда git в тест git diff --name-only origin/$BRANCH ответит строкой, содержащей файл сценария, если в нем есть локальные изменения, и l oop навсегда.

...