Ubuntu. Bash - Загрузка(обновление) репозитория с github.com

BASH в примерах
Загрузка(обновление) репозитория с github.com


Терминал(консоль, командная оболочка, CLI, командная строка) - текстовый интерфейс для общения с операционной системой. Bash - это командный процессор, работающий, как правило, в интерактивном режиме в текстовом окне. Bash также может читать команды из файла, который называется скриптом (или сценарием).
Рассмотрим команды используемые в скрипте, о котором говорится в видео выше.

Каждый bash-скрипт начинается с последовательности знаков "#!", который не интерпретируется как комментарий. Для того чтобы скрипт стал исполняемым, нужно дать нашему файлу права на исполнение, с помощью команды chmod
sudo chmod +x my_script.sh 
Команда chmodпрограмма для изменения прав доступа к файлам и директориям.
Аргумент +x означает установить права файла на исполнение, если же нужно снять права на выполнение то нужно вместо + написать -, то есть + добавляет определенные права, а - снимает определенные права. Более подробно в статье на википедии.
После этого, у нас есть готовый скрипт(правда пустой, ничего не выполняющий) который можно запускать на выполнение.
./my_script.sh 
Первая строка нашего скрипта содержит - #!/bin/bash
Идем дальше, перед выполнением наших команд, желательно очистить экран, но не обязательно, кому как нравится, значит первой командой будет команда clear, эта команда очищает терминал смещением всей информации вверх, по факту у нас чистое окно терминала, но мы можем пролистать и увидеть все то что происходило до этого. 
Перед тем как загрузить репозиторий, нам нужно проверить, существует ли директория с этим репозиторием или нет, если существует, значит нам нужно не загрузить а обновить его, а это уже другая команда.
Проверяем существование директории
#!/bin/bash
clear
if [ -d "my_folder" ]
then
  echo "my_folder is exist"
fi
При написании ветвлений с использованием операторов if нужно помнить что, условия задаются в квадратных скобках [ условие ], условие отделено от квадратных скобок пробелами, иначе работать не будет. 
  • [ -a FILE ] Правда если FILE существует.
  • [ -b FILE ] Правда если FILE существует и это специальный блоковый файл.
  • [ -c FILE ] Правда если FILE существует и это специальный знаковый файл.
  • [ -d FILE ] Правда если FILE существует и это директория.
  • [ -e FILE ] Правда если FILE существует.
  • [ -h FILE ] Правда если FILE существует и это символический ссылка.
  • [ -p FILE ] Правда если FILE существует и это named pipe (FIFO).
  • [ -r FILE ] Правда если FILE существует и он доступен на чтение.
  • [ -s FILE ] Правда если FILE существует и больше нуля.
  • [ -w FILE ] Правда если FILE существует и он доступен на запись.
  • [ -x FILE ] Правда если FILE существует и он доступен на исполнение.
  • [ -O FILE ] Правда если FILE существует принадлежит существующему пользователю
  • [ -G FILE ] Правда если FILE существует принадлежит существующей группе.
  • [ -L FILE ] Правда если FILE существует и это символический ссылка.
  • [ -N FILE ] Правда если FILE существует и был изменен с тех пор как был прочитан.
  • [ -S FILE ] Правда если FILE существует и это сокет.
  • и т.д.
Перед проверкой на существование директории, создадим переменную, которая будет хранить название нашей директории, для того чтобы в скрипте, при выводе информации мы могли уже использовать это значение для других действий(в частности для того чтобы зайти внутрь этой директории если она существует).
Переменные - это способ представления данных в различных языках программирования. Переменная не более чем метка - имя, назначенное области или группе областей памяти компьютера, где содержатся данные.
Надо различать имя переменной и ее значение.
var       - переменная
$var     - значение переменной
${var} - значение переменной
#!/bin/bash
clear

# создание переменной с именем var и значением 100
# при инициализации переменной по обе стороны 
# знака = не должно быть пробелов
var=100         

# вывод на терминал var, чтобы вывести значение переменной
# нужно сослаться на нее, с помощью символа $
echo var

# вывод значения переменной с именем var
echo $var
echo ${var}

# создание переменной с именем var1 и значением "A B   C"
var1="A B   C"  

# вывод - A B C
echo $var1      

# вывод - A B   C
# если вывод переменной заключен в кавычки то если в строковой
# переменной есть пробелы, они остаются на своих местах, 
# иначе игнорируются как в примере выше
echo "$var1"    

# вывод на терминал $var1, если переменная заключена в
# одинарные кавычки, то происходит запрет вывода значения 
# переменной и вывод происходит как есть, 
# что написано то и выводится
echo '$var1'    

# попытка объединить слово hello со значением переменной var1
# и словом world, на терминале увидим это - hello, так как
# интерпретатор после слова hello вывел значение переменной
# $var1world которой попросту нет. Чтобы сделать такое
# объединение используйте пример ниже
echo hello$var1world  

# здесь неоднозначностей нет, и выведется то что мы ожидаем, 
# но без лишних пробелов в значении переменной var1 
echo hello${var1}world  

# теперь все как доктор прописал
echo hello"${var1}"world 

С переменными познакомились. Теперь продолжим работу над нашим скриптом.
Нам нужна переменная которая хранит имя директории и нужна переменная которая хранит ссылку на репозиторий.
#!/bin/bash
clear

folder="c_beginning"
c_beginning_url="https://github.com/cCppProsto/c_beginning.git"

repo_url=$c_beginning_url
if [ -d ${folder} ]
then
  echo "folder ${folder} already exist!"
else
  echo "${folder} not exist"
  git clone $repo_url
fi
Наш скрипт, уже проверяет на наличие директории, если нет, то с помощью команды git clone мы скачиваем содержимое директории.
Далее было бы неплохо, выделять определенную информацию из всего текста что выводится на терминал, это можно сделать с помощью изменения цвета шрифта и его фона.
Если директория существует то эту информацию выведем красными буковками, если же не существует, то проверим, успешно ли выполнилась команда git clone, если да, то напишем об этом зелеными буковками.
Для установки атрибутов отображения информации на экране, таких как: жирный текст, цвет символов, цвет фона и т.п., с давних пор используются ANSI escape-последовательности. Эти последовательности широко используются в пакетных файлах DOS, эти же последовательности используются и в сценариях Bash.
Ключ -e, в команде echo, разрешает интерпретацию escape-последовательностей.
Числовые значения цвета в escape-последовательностях
Цвет Текст Фон
черный 30 40
красный 31 41
зеленый 32 42
желтый 33 43
синий 34 44
пурпурный 35 45
зеленовато-голубой 36 46
белый 37 47
\e[30;41m
30 - цвет текста
41 - цвет фона

Посмотрите на скрипт ниже, выполните его у себя, я думаю разобраться особого труда не будет.


#!/bin/bash
clear

# вывести текст разным цветом
echo -e "\e[30mblack"
echo -e "\e[31mred"
echo -e "\e[32mgreen"
echo -e "\e[33myellow"
echo -e "\e[34mblue"
echo -e "\e[35mmagenta"
echo -e "\e[36mcyan"
echo -e "\e[37mwhite"

echo

# вывести текст разным цветом + цвет фона - белый
echo -e "\e[30;47mblack"
echo -e "\e[31;47mred"
echo -e "\e[32;47mgreen"
echo -e "\e[33;47myellow"
echo -e "\e[34;47mblue"
echo -e "\e[35;47mmagenta"
echo -e "\e[36;47mcyan"
echo -e "\e[37;47mwhite"

echo

# вывести текст разным цветом + цвет фона - зеленый
echo -e "\e[30;42mblack"
echo -e "\e[31;42mred"
echo -e "\e[32;42mgreen"
echo -e "\e[33;42myellow"
echo -e "\e[34;42mblue"
echo -e "\e[35;42mmagenta"
echo -e "\e[36;42mcyan"
echo -e "\e[37;42mwhite"

# сбросить значение цвета текста и цвета фона до 
# значений по умолчанию, иначе вывод на терминал
# будет осуществляться последним цветом
echo -e "\e[39;49m" 
# или
# tput sgr0 

echo

#слово Hello жирным шрифтом
echo -e "\033[1mHello\033[0m"

#слово Hello подчеркнутым шрифтом 
echo -e "\033[4mHello\033[0m"

# можно еще вот так, так даже проще
bold=$(tput bold)
underline=$(tput smul)
normal=$(tput sgr0)

echo "this is ${bold}bold${normal} but this isn't"
echo "this is ${bold}${underline}bold and underline${normal} but this isn't"
Самое главное не забывать сбрасывать до первоначальных настроек. И лучше командой
tput sqr0
Надеюсь с цветом немного прояснилось, идем далее, помимо цвета нам нужно узнать, программа выполнилась успешно или нет? Как мы знаем любая программа на Си или С++ начинается с функции int main { return 0;} и признаком того что программа завершилась НЕ аварийно(то есть выполнилась успешно) есть код возврата который равен 0. Так вот этот код возврата мы и можем узнать, то есть выполнить команду(программу) и спросить, ты с каким кодом завершилась, если код 0 то все хорошо, если же нет, то мы понимаем что программа завершилась либо аварийно, либо она отработала не правильно, и намеренно вернула код отличный от нуля.
В bash вывести этот код последней выполенной программы можно так - echo $?, то есть $? возвращает завершающий код последней выполненной программы.
Итак, с учетом всего сказанного наш скрипт принимает следующий вид:
#!/bin/bash
clear

folder="c_beginning"
c_beginning_url="https://github.com/cCppProsto/c_beginning.git"

folder="c_beginning"
repo_url=$c_beginning_url
if [ -d ${folder} ]
then
  echo -e "\e[31m${folder} already exist!\e[39m"
  echo "try to update"
else
  echo "${folder} not exist"
  git clone $repo_url
  if [ $? -eq "0" ] 
  then
    echo -e "\e[32m${folder} repo clone is Ok!\e[39m"
  fi
fi
Ах да... тут еще есть новая конструкция в if -  $? -eq "0"
Если нам нужно сравнить целочисленные значения, используйте следующий синтаксис
сравнение целых чисел

  • -eq   равно                         if [ "$a" -eq "$b" ]
  • -ne   не равно                    if [ "$a" -ne "$b" ]
  • -gt    больше                      if [ "$a" -gt "$b" ]
  • -ge   больше или равно    if [ "$a" -ge "$b" ]
  • -lt    меньше                      if [ "$a" -lt "$b" ]
  • -le    меньше или равно    if [ "$a" -le "$b" ]
  • <      меньше (внутри двойных круглых скобок )                   (("$a" < "$b"))
  • <=    меньше или равно (внутри двойных круглых скобок)  (("$a" <= "$b"))
  • >      больше (внутри двойных круглых скобок)                     (("$a" > "$b"))
  • >=    больше или равно (внутри двойных круглых скобок)   (("$a" >= "$b"))
сравнение строк

  • =      равно           if [ "$a" = "$b" ]
  • ==    равно           if [ "$a" == "$b" ]
  • !=     не равно      if [ "$a" != "$b" ]
  • <      меньше, в смысле величины ASCII-кодов if [[ "$a" < "$b" ]] if [ "$a" \< "$b" ] Обратите внимание! Символ "<" необходимо экранировать внутри [ ].
  • >     больше, в смысле величины ASCII-кодов if [[ "$a" > "$b" ]] if [ "$a" \> "$b" ]
  • -z строка "пустая", т.е. имеет нулевую длину if [ -z "$a" ]
  • -n строка не "пустая".  Оператор -n требует, чтобы строка была заключена в кавычки внутри квадратных скобок. if [ -z "$a" ]
ВСЕГДА заключайте проверяемые строки в кавычки

Наш скрипт, уже проверяет на наличие директории, если нет, то с помощью команды git clone мы скачиваем содержимое директории и проверяем успешность выполения скрипта, если все хорошо подсвечиваем текст зеленым, если директория есть то сообщает об этом красным текстом.
Теперь нам осталось сделать следующее - если директория есть, перейти в нее, и проверить на наличие новых изменений на сервере github если они есть, то обновить наш существующий репозиторий ПЕРЕЗАПИСЫВАЯ(УДАЛЯЯ) все наши локальные изменения, я остановлюсь только на одном моменте - это спросить пользователя(до этого нужно проверить есть ли обновления) обновлять локальные исходники или нет. Нужно запросить у пользователя ответа на вопрос, нажатием кнопочки n или y. Это сделаем с помощью команды -
read -r -p  "Update [y/n] " answer
которая говорит что нужно прочитать со стандартного потока ввода строку после вывода "Update [y/n] " в новую переменную answer, ввод остается на той же строке на которой написан вопрос.
Можно и способом ниже, тогда происходит чтение только одного символа.
#!/bin/bash
clear

read -n 1 answer
echo
echo "answer = '${answer}'"

Итак, применив все наши знания(не учитывая команды git, возможно, когда-то, в ближайшем дальнем будущем, по ним будет блог... :-( ), получаем следующий скрипт
#!/bin/bash
clear

folder="c_beginning"
c_beginning_url="https://github.com/cCppProsto/c_beginning.git"

folder="c_beginning"
repo_url=$c_beginning_url
if [ -d ${folder} ]
then
  echo -e "\e[31m${folder} already exist!\e[39m"
  echo "try to update"
  cd ${folder}
  git remote update
  count=$(git rev-list HEAD...origin/master --count)
  if [ $count -gt "0" ]
  then
    echo -e "\e[31m"
    read -r -p  "Update [y/n] " answer
    echo -e "\e[39m"
    if [ "$answer" = "y" ] 
    then
      git fetch --all
      git reset --hard origin/master
    fi
  fi
  cd -
else
  echo "${folder} not exist"
  git clone $repo_url
  if [ $? -eq "0" ] 
  then
    echo -e "\e[32m${folder} repo clone is Ok!\e[39m"
  fi
fi
Что осталось сделать вам, запихнуть это в функцию(самостоятельно почитать о функциях в bash) и вызвать функции для создания(обновления) всех нужных репозиториев.

На сегодня все! Возможны дополнения! Спасибо за внимание.

Комментарии

  1. Божественно! Все просто, подробно и доступно. Можно (особенно для цветов) шпаргалку сделать!

    ОтветитьУдалить

Отправить комментарий