Shell-script

                                                           Все, что можно сделать при помощи оболочки, sed и awk, уже сделано.!

                                                                                                                                                               Brian W. Kernighan    

Большая часть того,что требуется для пользования, администрирования *nix систем, делается с помощью одной правильной команды или нескольких команд в конвейере выполненных в терминале командного интерпретатора, как и файла с набором этих команд под названием сценарий, все командные интерпретаторы начиная с sh полноценные высокоуровневые языки программирования (считая все утилиты системы его операторами) и  при таком подходе от sh требуется совсем немного: возможность вызывать утилиты, возможность свободно манипулировать результатом их работы и несколько алгоритмических конструкций  (условия и циклы).  Надо заметить, что писать сценарии для bash - непрактично, так как исполняться они смогут лишь при помощи bash.

PS: Оператор (statement), что совершенно очевидно как минимум не корректно, лучше инструкция (наименьшая часть языка, команда или их набор), скрипт представляет собой построчный набор инструкций, они же команды (что вообще полная фигня), конечно это не команды, а именно инструкции, это наиболее удачное определение, поскольку более общее, другое название "продцедуры", от того и прграммирование продцедурное или императивное. 

На практике использование sh, совместимость с которым объявлена и в bash, и в zsh, и в ash (наиболее близком по возможностям к sh), и в других командных интерпретаторах, приведет к тому что выполняться эти сценарии смогут любым из sh-подобных интерпретаторов, и если быть точным sh это не только одна из оболочек, это прежде всего стандарт POSIX в чем есть совершенно определенные преимущества, ну а если говорить про скриптовые языки , то лучше говорить про Python ( скриптовый  язык программирования общего назначения), в целом выбор оболочки это личные предпочтения коррелируемые с навыками и задачами которые предполагается решать.

    Если первыми двумя символами сценария будут "#!" (под названием sha-bang, hashbang ("#" - октоторп, восемь концов, плюс восклицательный знак)), система истолкует следующие за ними символы как абсолютное имя утилиты, то есть в качестве префикса добавит в структуру выполнения путь до исполняемого файла интерпретатора, который выполнит сценарий. Желательно понимать, что строчка #!/bin/bash, выполненная в терминале командного интерпретатора bash (как и любого другого) даст рождение еще одному процессу bash, что необходимо всегда учитывать (например используя переменные). source 

Что касается скрипта "как такового" то наличие этой волшебной строчки: #!/bin/bash, совсем не обязательно, обязательным является условие что файл "исполняемый" chmod +x, и переменная PATH: .... содержит дирректорию с которой этот файл будет выполнен, команда source [.] все эти "условности" упразнит (как и запись "./": точка слэш (точнее не все, файл должен быть исполняемым в случае "./" , в случае source не обязательно), для "source"  необходимо указание пути (если не из текущей директории), в этом в том числе разница с записью "./" где текущая дирректория указывается явно и выполнить "./" можно только из текущей директории и при том, что файл исполняемый, вообщем если надо загрузить функцию или выполнить файл в текущей оболочке то надо использовать source. И зачем вообще нужна "точка слеш", если с тем же успехом можно использовать имя интерпретатора в качестве оператора, это тайна! ....и тайна сия велика.

PS: Учитывая, что в /etc/passwd так или иначе все равно прописан интерпретатор "по умолчанию", система попытается выполнить исполняемый файл указанным интерпретатором, однако корректная форма скрипта это наличие "hashbang" в его начале, тем более что появляется возможность выбрать и другой интерпретатор, так или иначе не стоит добавлять системы лишние проблемы и следует указать не посредственно нужный вам интерпретатор команд.

PS: И еще раз о "source" и "./", используя source мы грубо говоря загружаем код в оперативную память, в чем можно убедиться посмотрев вывод команды "set" то есть посмотрев окружение интерпретатора, и далее мы работаем с оперативной памятью, потому и функцию загружают через "source", в случае "./" ...то же нечто подобное?, как это реализовано?, это вопрос к Ботаникам, а нам достаточно знать, что для исполнения кода мы можем использовать имя интерпретатора , в качестве аргумента, запись "./", предварительно сделав код исполняемым и "source". Что бы все это было не так замысловато в *nix системах существует переменная PATH:

$ echo $PATH

/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/usr/local/games:/usr/games


$ echo $PATH | awk 'BEGIN{pwd=ENVIRON["PWD"];RS=":";FS="\n"}!$1{$1=pwd}$1!~/^\//{$1=pwd"/"$1}{print $1}'

/usr/local/sbin

/usr/local/bin

/usr/sbin

/usr/bin

/usr/local/games

/usr/games

.. и помещая сценарий в которую, предварительно сделав его исполняемым:chmod +x, получаем исполнение кода.

$ cat > eco

date;pwd

^C

$ . eco

123 456 789

Fri Jan 10 08:31:01 MSK 2020

/home/user

$ ./eco

bash: ./eco: Permission denied

$ chmod +x eco

$ ./eco

123 456 789

Wed 06 Oct 2021 12:01:13 PM MSK

/home/user

Ну и конечно всю эту "хрень" можно записать одной строкой в виде "выражения":

$ echo "123 456 789";date;pwd

123 456 789

Thu Nov  9 04:51:12 PM MSK 2023

/home/user

$ cd Desktop

~/Desktop$ . ~/eco

123 456 789

Sun Jan 22 02:49:41 PM MSK 2023

/home/user/Desktop

~/Desktop$ ./ ~/eco

bash: ./: Is a directory

Не следует так же забывать, что используя source между псевдонимом "точка" и именем файла необходим пробел:

$ .eco

bash: .eco: command not found

Скрипты или «сценарии»  -- Интерпретируемые программы, программы для которых, как правило, не применяется процесс компиляции и которые интерпретируются операционной системой или специальными программами-интерпретаторами (bash, sh ...).

Скриптовой язык, scripting language — высокоуровневый язык сценариев, кратких описаний действий, выполняемых системой. Разница между программами и сценариями сильно размыта. Сценарий — это программа, имеющая дело с готовыми программными компонентами. Типы сценарных языков; Perl, PHP, Python, Rubby и тд. (языки "общего назначения", в отличии от "прикладных" типа bash, sh, ksh ...., и "встроенных" типа: lua, emacs lisp, 1С предприятие ...., но вся эта классификация весьма условна) 

Структура выполнения скрипта последовательная, как и всего императивного программирования, то есть, пока не будет завершено какое-то действие (выполнение инструкции) следующее действие не начнет выполняться. Сценарий не относится к компилируемым программам, поскольку он интерпретируется построчно. Сценарий просматривается интерпретатором команд в направлении сверху вниз. Вероятно не стоит упоминать то, что для выполнения скрипта он должен быть исполняемым (chmod) и вспомнить про переменную PATH (echo $PATH) (export PATH=$PATH: ...)

Нужно так же понимать что такое «состояние» (condition - состояние, условие, обстоятельства). Состояние характеризуется тем, что описывает переменные свойства объекта. Состояние стабильно до тех пор, пока над объектом не будет произведено действие; если над объектом будет произведено некоторое действие, его состояние может измениться. Применительно к bash - состояние есть не что иное как результат выражения в логической форме, представленное как ложь или истина (true или false). 

Если "грамотно", то надо говорить о "состоянии вычислителя" и изменении его состояния посредствам инструкций/продцедур, одна из краеугольных инструкций продцедурного или императивного програмирования это инструкция проверки условий if, then, else, о чем ниже и инструкция присваивания в концепции изменяемых переменных. Императивный вычислитель (то есть традиционный, то есть последовательный Sequential models) можно понимать как "машину состояний", конечных, бесконечных, дискретных автоматов (в теории алгоритмов), в итоге скрипт представляется как совокупность механизмов условного исполнения, механизмов циклов, последняя инструкция  завершает скрипт, процесс вычисления. (Sequential — Functional — Concurrent, models of computation)

Проверка условий

    Любой язык программирования включает в себя условные операторы, предназначенные для проверки условий, чтобы выбрать тот или иной путь развития событий в зависимости от этих условий. В Bash, для проверки условий, имеется команды test, expr, различного вида скобочные операторы и условный оператор if/then.

https://www.opennet.ru/docs/RUS/Проверка условий 

https://drive.google.com/file/=sharing  <<-----Функции сравнения и тестирования в Bash

Test

    test — UNIX-утилита для проверки типа файла и сравнения значений. Возвращает код возврата Ø (истина) или 1 (ложь) в зависимости от вычисления выражения. Выражения могут быть как унарными (с одним операндом), так и бинарными (с двумя операндами). [8] Унарные выражения часто используются для проверки статуса файла.

Команда test используется для тестирования строк, проверки прав доступа к файлу, численных данных и прочее, эта команда хорошо согласуется с условной конструкцией if, then, else, по этому  как правило все конструкции if, then, else начинаются с синонима команды test - '[' (левой скобки): if [ .... ]: then, то есть с команды test. В целом используется в несложных shell-скриптах для проверок разного рода.

test условие

или равнозначно:  [ условие (conditional expression-условное выражение) ]  (Пробелы с обеих сторон от условия обязательны, поскольку скобка, то есть test  это слово, двойная скобка, то же слово, причем "ключевое", и пробелы между словами ясно дело обязательны)

Если вы хотите реализовать сложное условие:    if [ a = b ] && [ c = d ] равно if test a = b && test c = d но не так if [[ a = b ] && [ c = d ]]


'[[' - расширенная версия '[', обеспечивающая дополнительную функциональность, кроме того двойные скобки являются ключевым словом (в отличии от test и его синонима [, являющейся командой), внутри двойных скобок могут быть использованы операторы || (или), && (и), группирующие команды по принципу логическог И/ИЛИ. Как и обычная версия должна иметь закрывающуб скобку ']]' как символ конца команды, как и в обычной версии, пробелы внутри внутренних скобок обязательны, есть "мнение" что оценка условий внутри двойных скобок "несколько" быстрее. Кроме всего есть еще одна особенность в использовании одинарных и двойных скобок, если использовать версию с одной скобкой то условия (например переменные) следует заключать в двойные кавычки в случае двойных скобок не обязательно, что логически вытекает из их различия, например:

$ [ $1 = "qwerty" ] bash: [: =: unary operator expected; будет равно [   = "qwerty"]; , по этому вернее так  $ [[ $1 == "qwerty" ]]; , естественно в добавок ко всему существуют разные реализации как и версии оболочек  и что работает в "sh", не всегда так работает в "bash", по этому во избежании ошибок типа: bash: [: =: unary operator expected, либо "заковычивать", либо двойные скобки.

Пример:  test -e <filename>   проверка существования файла

                [ -e <filename> ]     эквивалентная запись

PS: Для сокращения кода сценария используют парный оператор '[' как синоним test, то есть левая скобка выполняет те же действия, что и команда  test  Парный оператор требует пробела между скобками и аргументами (условиями), потому что [ (скобка, левая скобка) является командой оболочки, а POSIX требует пробела между командой и ее аргументами, как и завершения команды обратной скобкой ']' (правая скобка завершает проверку условия, в старых версиях не является абсолютно не обходимой).

$ type [

[ is a shell builtin

$ type ]

bash: type: ]: not found

$ type [[

[[ is a shell keyword

$ type ]]

]] is a shell keyword

$ type test

test is a shell builtin

$

Команда testвстроенная команда ее единственное предназначение заключается в возврате кода завершения. Она ничего не выводит и не изменяет файлы.

Оболочка хранит код завершения последней программы в переменной $?: echo $?

Команда test и [[expr]] 

Опции часто используемые при проверки файлов

Условия:

Логические операторы:

Логическая операция 

    Логическое AND, возвращает истину, если обе части оператора принимают истинное значение

    Логическое OR, возвращает истину, если какая-либо из частей оператора может принимать  истинное значение 

!       Логическое NOT, возвращает истину, если условие ложно

В качестве примера проверим установку прав на запись или выполнение скрипта ¨father¨:

$ test -w father

$ echo $?

Ø

$ [ -w father ]

$ echo $?

Ø

$ test -x father

$ echo $?

Ø

$ [ -x father ]

$ echo $?

Ø

Возвратились нули, что есть правда:

$ ls -l father

-rwxr-xr-x 1 imint imint 233 авг. 6 16:56 father

Рассмотрим два файла, для примера работы логических операторов:

father    --->> исполняемый

param3 --->> не исполняемый

$ ls -l father

-rwxr-xr-x 1 imint imint 233 авг. 6 16:56 

$ ls -l param3

-rw-r--r-- 1 imint imint 68 авг. 6 20:49 

$ [ -x father -a -x param3 ]

$ echo $?

1

$ [ -x father -o -x param3 ]

$ echo $?

Ø

    возвращает истину, если обе части оператора принимают истинное значение

    возвращает истину, если какая-либо из частей оператора может принимать  истинное значение 

Проверка строк

     http://www.opennet.ru/docs/RUS/bash_scriptis

    Проверка строк является важным этапом при отслеживании ошибок. Значение этого этапа повышается, если проверяются вводимые пользователями данные либо выполняется сравнение переменных. Чтобы проверить строки, достаточно выбрать один из пяти форматов.

test "строка"

test оператор_строки "строка"

test "строка" оператор_строки "строка" '

[ оператор_строки строка ]

[ строка оператор_строки строка ]

В качестве выражения оператор_строки могут использоваться следующие операторы:

Пример: присвоено ли переменной среды EDITOR нулевое значение?

$ [ -z $EDITOR ]

$ echo $?

1 <<----- НЕТ

строка не является нулевой?

$ [ -n $EDITOR ]

$ echo $?

Ø <<----- ДА

$ echo $EDITOR

vim

$

Оператор -n требует, чтобы строка была заключена в кавычки внутри квадратных скобок. Как правило, проверка строк, не заключенных в кавычки, оператором ! -z, или просто указание строки без кавычек внутри квадратных скобок (см. Пример 7-6), проходит нормально, однако это небезопасная, с точки зрения отказоустойчивости, на практике, всегда заключайте проверяемую строку в кавычки. равны ли две строки?

$ [ "$EDITOR" = "$HOME" ]

$ echo $?

1 <<----- НЕТ

$

строки не равны?

$ [ "$EDITOR" != "$HOME" ]

$ echo $?

Ø <<----- ДА

Проверка чисел

    Для сравнения чисел можно воспользоваться операторами другого рода. Общий формат:

 "число" числовой_оператор "число"

или

[ "число" числовой_оператор "число" ]

где в качестве выражения числовой оператор могут фигурировать следующие операторы:

Необязательно для выполнения проверки обращаться к переменной: можно сравнивать и числа, но в этом случае следует применять кавычки:

$ [ "1000" -le "900" ]

$ echo $?

1

$ [ "1000" -gt "900" ]

$ echo $?

Ø

  Можно также комбинировать и тестировать выражения с помощью логических операторов. При этом следует пользоваться только одной парой квадратных скобок — не применяйте две пары скобок. Если не учитывать этого замечания, отобразится сообщение об ошибке — “too many arguments” (слишком много аргументов):

$ [ "1000" -gt "900" -a "1000" -ne "900" ]

$ echo $?

Ø <<----- ДА

$

Expr

expr - evaluate expressions (вычесление выражения)

https://ru.wikipedia.org/wiki/Expr

http://www.opennet.ru/man

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

expr определяет тип операнда (целое число или строка) по применяемой к нему операции.

ехрг используется в основном для проверки целочисленных значений, но может применяться также и при обработке строк. Общий формат команды ехрг:      ехрг аргумент   оператор аргумент

$ expr 500 + 600

1100

$ expr 30 / 2

15

$ expr 50 "*" 3

150

$ expr 1200 / 2 / 3

200

$ expr 1200 / 2 / 3 "*" 5

1000

$

Выполнение командного файла:

x=7 y=2                                                    

a=`expr $x + $y`      ; echo a=$a a=9

a=`expr $a + 1`       ; echo a=$a a=10

b=`expr $y - $x`      ; echo b=$b b=-5

c=`expr $x '*' $y`    ; echo c=$c c=14

d=`expr $x / $y`      ; echo d=$d d=3

e=`expr $x % $y`      ; echo e=$e e=1

======================================================================

Аргументы команды test подвергаются обычным подстановкам и разбиением на слова, что часто приводит к ошибкам:

[ "A" > "B" ]       # неправильно

[ "A" \> "B" ]      # правильно

[ $A = HELLO ]      # ошибка если переменная A не определена, содержит пробелы или символы подстановки

[ "$A" = HELLO ]    # ошибка если $A начинается с минуса. Например -eq

[ x"$A" = xHELLO ]  # прием, позволяющий корректно провести сравнение

Для того, чтобы преодолеть указанные проблемы в язык введена синтаксическая конструкция [[expr]], которая разбирает выражение expr по тем же правилам, что и команда test, но при этом внутри скобок не производится подстановка имен файлов, перенаправление ввода/вывода и разбиение содержимого переменных на слова.

[[expr]] в отличие от [ expr ] не является независимой командой, что можно увидеть на примерах:

\[ -e file \]        # нормально

[ -e file ]          # то же самое

\[\[ -e file \]\]  # [[: command not found

[[ -e file ]]       # нормально

Команда test и [[expr]] 

Банальный пример, где то в "инете" вы нашли скрипт, отредактировали .... но выполняется он с ошибками (если вообще выполняется), часто это не совпаление кодировок ASCII, обычное дело при копировании из PDF, и все это просто проверить с помощью test  или [[expr]], в следующем примере давайте заменим символ "о" на EN и RU кодировки в обоих полях;

Две строки равны?

$ [ "о4+2+1" == "o4+2+1" ]

$ echo $?

1 нет

$ [ "expr о4+2+1" == "expr o4+2+1" ]

$ echo $?

1 нет

Приращение переменной цикла

    Команда ехрг выполняет приращение переменной цикла. Сначала переменной цикла присваивается начальное значение нуль. Затем добавляется единица. Кавычки применяются для обозначения подстановки команд. Выводимые данные, полученные с помощью команды ехрг, присваиваются переменной цикла.

$ LOOP=0

$ echo $LOOP

Ø

$ LOOP=`expr $LOOP + 1`

$ echo $LOOP

1

$

Проверка численных значений

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

$ expr rr + 1

expr: нецелочисленный аргумент

$

Итак, необходимо передать значение переменной (не важно, какой именно), выполнить любую арифметическую операцию и направить выводимые данные в /dev/null. Затем достаточно проверить код завершения последней команды. Если код равен нулю, тогда мы имеем дело с числом; любое другое значение свидетельствует о том, что данное значение не является числом точнее Выходной статус равен:

Ø , если ВЫРАЖЕНИЕ не пустое и не Ø ; 

1, если ВЫРАЖЕНИЕ пусто или Ø ; 

2, если ВЫРАЖЕНИЕ синтаксически неверно;

3, если случится ошибка.

$ LOOP=20

$ expr $LOOP + 1 > /dev/null 2>&1

echo $?

Ø <--- это число

$ LOOP=zzzz

$ expr $LOOP + 1 > /dev/null 2>&1

$ echo $?

2 <--- это не число (ВЫРАЖЕНИЕ синтаксически неверно)

Команда ехрr также возвращает свой собственный код завершения. К сожалению, значение этого кода противоположно значению кода завершения последней команды. Команда ехрr отображает единицу, если результат проверки истинен, и любое другое значение, если допущена какая-либо неточность или ошибка. Ниже приводится пример проверки двух строк на предмет равенства. В ходе проверки требуется выяснить, является ли строка zzzz равной строке zzzz.

$ LOOP=zzzz

$ expr $LOOP = "zzzz"

1

$ echo $?

Ø

$

Команда ехрr возвращает единицу но, все идет хорошо. Теперь выполним проверку с помощью кода завершения последней команды: возвращается значение нуль. В ходе проверки выяснилось, что строка ¨zzzz¨ действительно равна строке ¨zzzz¨.

Надо помнить, что при использовании в оболочке некоторые операторы должны быть взяты в кавычки. Если оба аргумента являются числами, то производится арифметическое сравнение, иначе они сравниваются как строки. Сопоставления с образцом возвращают совпавшую строку между \( и \) или пустую строку; если \( и \) не использованы, то возвращается число совпавших символов.

$ expr --help

Отладка сценариев

Для отладки скриптов bash можно использовать встроенный набор команд. Можно включать и выключать режим отладки используя следующие команды внутри скрипта:

set -x - (set -o xtrace) показывать команды и параметры, которые выполняются;

set -v - (set -o verbose) печатает входные строки сразу по мере их считывания;

set -f - (set -o noglob) отключается генерация имени файла с помощью метасимволов (подстановка).

http://wiki.dieg.info/bash

http://www.ylsoftware.com <<<< ------ 

PS: Не лишне заметить, что все что написано выше не претендует ни на "учебник" ни на "методическое пособие" по написанию скриптов, поскольку с точки зрения методологии, изучение структуры скриптов видимо надо начинать с "Управляющих конструкций" (которые ниже по сылке), частью которых и явлюются операторы проверки условий, но наверное следует изначально усвоить то что называется условие conditionalconditional expression  — условное выражение, их проверка или тестирование, вообщем это те логические "кирпичи" из которых скрипт и складывается, тривиальный и простой Shell - скрипт, владение языками интерпретаторов команд часть философии UNIX, программирование и UNIX можно считать не разрывно связанным, языки оболочек могут показаться примитивными, плохо масштабируемыми, но они и не расчитаны для достижения "великих задач",  это часть операционной системы, средство понять что такое язык програмирования "здесь и сейчас", инструмен управления и решения текущих задач администрирования системы, и как следствие существуют "скриптовые языки" типа Perl, PHP, Python, Rubby и тд. и это уже другой более высокий уровень, того же императивного программирования. 

"...Bash — орудие "судного дня", штатный инструмент системного администратора."