В Bash я создаю скрипт обновления, как обновить скрипт обновления - PullRequest
0 голосов
/ 09 января 2019

Я создаю небольшой скрипт для обновления файлов приложения на Raspberry Pi.

Будет выполнено следующее:

  1. Скачать zip-файл файлов приложений
  2. Распакуйте их
  3. Скопируйте каждый файл в нужное место и сделайте его исполняемым и т. Д. При необходимости.

У меня проблема в том, что один из файлов - updatescript.sh.

Я читал, что опасно обновлять / изменять скрипт bash во время его выполнения. См. Редактирование сценария оболочки во время его работы

Есть ли хороший способ добиться того, что я пытаюсь сделать?

Ответы [ 2 ]

0 голосов
/ 09 января 2019

То, что вы прочитали, сильно преувеличено.

Совершенно безопасно перезаписать сценарий оболочки на месте, mv вставив в него другой файл. При этом старый дескриптор файла остается действительным, ссылаясь на исходный неизмененный файл. содержание. То, что вы не можете безопасно сделать, это отредактировать существующий файл на месте.

Итак, ниже все в порядке (и это то, что делают все ваши инструменты обновления поставщиков ОС, такие как RPM):

#!/usr/bin/env bash
tempfile=$(mktemp "$BASH_SOURCE".XXXXXX)
if curl https://example.com/whatever >"$tempfile" &&
   curl https://example.com/whatever.sig >"$tempfile.sig" &&
   gpgv "$tempfile.sig" "$tempfile"; then
  chown --reference="$BASH_SOURCE" -- "$tempfile"
  chmod --reference="$BASH_SOURCE" -- "$tempfile"
  sync # force your filesystem to fully flush file contents to disk
  mv -- "$tempfile" "$BASH_SOURCE" && rm -f -- "$tempfile.sig"
else
  rm -f -- "$tempfile" "$tempfile.sig"
  exit 1
fi

... тогда как это рискованно:

curl https://example.com/whatever >/usr/local/bin/whatever

Так что сделайте первое, а не второе: при загрузке новой версии вашего скрипта запишите это в другой файл и переименуйте его поверх оригинала только после успешной загрузки. Это то, что вы хотите сделать, чтобы обеспечить атомарность.

(Есть также некоторые демонстрации методов проверки подписи кода выше, потому что, ну, они вам нужны при создании средства обновления. Вы не пытались бы распространять код с помощью автоматической загрузки без проверки подписи, верно «Потому что именно так один простой прорыв к вашему веб-серверу приводит к тому, что каждый ваш клиент получает 0wned. Выше ожидается, что открытая сторона ваших ключей для подписи кода будет в ~/.gnupg/trustedkeys.gpg, но вы можете поставить trustedkeys.gpg в любом и укажите на него переменную окружения GNUPGHOME).


Даже если вы не напишите свой код обновления безопасно, риск по-прежнему тривиален, чтобы уменьшить его. Если вы переместите основную часть вашего скрипта в функцию, так что она должна быть полностью прочитана перед любая его часть может быть выполнена, но в тот момент, когда начинается выполнение, нет той части файла, которая еще не была прочитана.

#!/usr/bin/env bash
main() {
  echo "Logic all goes here"
}; { main; exit; }

Поскольку { main; exit; } является частью составной команды, синтаксический анализатор читает exit до того, как он начнет выполнять main, поэтому гарантируется, что после завершения работы main дальнейшее содержимое исходного файла не будет прочитано, , даже если какой-то будущий выпуск bash не обрабатывал ввод строки построчно .

0 голосов
/ 09 января 2019

В основном, делайте что-то вместе

shouldbe="/tmp/$(basename "$0")"
if [ "$0" != "$shouldbe" ]; then
    cp "$0" "$shouldbe"
    exec env REALPATH="$0" "$shouldbe" "$@"
fi
  1. Проверьте, не запускаете ли вы из временного каталога
  2. Если это не так, скопируйте себя и повторно запустите из временного каталога

Вы даже можете передавать некоторые переменные / состояния, используя переменные или аргументы среды. Затем вы можете обновить себя, используя простой cp, так как старый путь больше не используется (или даже не открыт).

 cp "new_script_version.sh" "$REALPATH"

Сценарий просто выглядит так:

#!/bin/bash

# we need to be run from /tmp directory
shouldbe="/tmp/$(basename "$0")"
if [ "$0" != "$shouldbe" ]; then
    cp "$0" "$shouldbe"
    exec env REALPATH="$0" "$shouldbe" "$@"
fi

echo "Updatting...."
echo "downloading zip files"
echo "unziping zip files..."
echo "Copying each zip files etc."
cp directory"new_updatescript.sh "$REALPATH"
echo "Update succedded"

Live / тестовая версия доступна на tutorialspoint .

Можно также реализовать на всякий случай некоторую flock блокировку скриптов.

...