BASH. Условия, циклы, функции.

Условия

Базовая форма.

if [[ "$some_variable" == "good input" ]]; then
echo "You got the right input."
elif [[ "$some_variable" == "ok input" ]]; then
echo "Close enough"
else
echo "No way Jose."
fi

fi придает предложению немного экзотический вид

Если (if) что-то является истиной, тогда (then) выполни вот это. В противном случае проверяй другие условия по порядку и делай то же самое.

Если ни одно из условий не сработало, выполни последнее указание.

Можно отбросить одну или все ветки elif, а также ветку else, если в них нет нужды!

Булевы операторы.

! - нет

&& - и

|| - или

Например.

if [[ "$name" == "Ryan" ]] && ! [[ "$time" -lt 2000 ]]; then

Иногда квадратные скобки могут быть одинарными

if [ "$age" -gt 30 ]; then

А иногда и круглыми.

if (( age > 30 )); then

А иногда их вообще нет

if is_upper "$1"; then

Есть магические слова, которые стоят за работой if в Bash: коды выхода.

Содержимое сразу после if может быть вообще любой командой, если она дает код выхода.

Если команда возвращает код выхода 0 (в Bash это код успешно выполненной операции), тогда запускается код внутри ветки then.

Существует специальная команда – [ (левая квадратная скобка). Она является синонимом команды test, и является встроенной командой (т.е. более эффективной, в смысле производительности). Эта команда воспринимает свои аргументы как выражение сравнения или как файловую проверку и возвращает код завершения в соответствии с результатами проверки (0 – истина, 1 – ложь).

Начиная с версии 2.02, Bash предоставляет в распоряжение программиста конструкцию [[ … ]] расширенный вариант команды test, которая выполняет сравнение способом более знакомым программистам, пишущим на других языках программирования. Двойные квадратные скобки работают в целом так же, как и одинарные квадратные скобки, но имеют дополнительные возможности вроде лучшей поддержки регулярных выражений.

Круглые скобки (( … )) и предложение let … так же возвращают код 0, если результатом арифметического выражения является ненулевое значение.

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

count=0
(( count++ ))

Если результат в скобках равен нулю, возвращается код выхода 1 (по сути, нулевой результат это «ложь»). Любой другой результат считается истиной, при нем код выхода будет 0.

Знаки «больше» и «меньше» внутри круглых скобок работают прекрасно когда как в квадратных путаются с переправлением вывода.

Примеры.

Пытаемся изменить текущий каталог.

if cd /tmp
then
    echo "Success!!!"
    ls -l
fi

Работаем с конвеером команд.

if ls | grep pdf
then
    ....
fi

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

if ls | grep pdf | wc

Проверяем на существование файл.

if [[ -e $FILENAME ]]
then
    ...
fi

Список дополнительных тестов.

Операторы проверки файлов

-d - существует ли каталог

-e - существует ли файл

-r - существует ли файл и доступел ли он для чтения

-w - существует ли файл и доступел ли он для записи

-x - существует ли файл и является ли он исполняемым

Числовые тестовые операторы.

-eq - равно

-gt - больше

-lt - меньше

Двойные скобки

if (( VAL < 12 ))

Внутри скобок не нужен оператор $ для получения значения за исключением позиционных параметров $1 $2 и т.д. (чтобы не путать их с константами 1 2 и т.д.)

В двойных скобках любое ненулевое значение является истинным и только ноль - ложным.

Анализ результата выполнения предыдущей команды

if (( $? )) ; then ... ; fi

Разделение команд.

Команды в BASH можно писать в одну строку разделяя ; && или ||

Примеры

cd $DIR; ls - команды выполнятся одна за одной

cd $DIR && ls - команда ls выполнится в случае успешной первой

cd $DIR || ls - команда ls выполнится в случае СБОЯ первой

Можно использовать тесты [[ ]] не только в конструкциях if

[[ -d $DIR ]] && ls "$DIR"

Циклы

Цикл с оператором while по структуре похож на if в том смысле, что для принятия решения он может принимать одну команду или конвейер команд, выдающих true или false .

В нем также можно использовать квадратные или круглые скобки, как в предыдущих примерах.

В некоторых языках программирования фигурные скобки (символы {} ) предназначены для группировки операторов, которые находятся в теле цикла while .

В таких языках программирования, как Python, тело цикла и его операторы определяются отступом.

В bash операторы сгруппированы между двумя ключевыми словами: do и done .

Вот простой цикл while :

i=0
while (( i < 1000 ))
do
    echo $i
    let i++
done

let - это встренная функция BASH которая выполняет арифметические операции.

Аналогично можно воспользоваться круглыми скобками.

(( i++ ))

Синтакс

let arg [arg ...]

При этом let выполняет арифметические операции со всеми переданными аргументами слева на право.

Список некоторых арифметических операторов.

** var++ var– ++var –var ** - сложение, вычитание, причем с пред и пост интерпретацией.

! - логическое отрицание

** *, /, % ** - умножение, деление, модуль

<<, >> - побитовое смещение

ссылка на документацию

Вот более сложный цикл while , который выполняет команды как часть своего условия:

while ls | grep -q pdf
do
    echo -n 'there is a file with pdf in its name here:'
    pwd
    cd ..
done

grep -q - при поиске молчаливо игнорировать ошибки

echo -n - не выводить символы переноса строк

Цикл for также доступен в bash, причем в трех вариантах.

Вариант 1.

for ((i=0; i < 100; i++))
    do
    echo $i
done

Вариант 2.

for ARG
    do
    echo here is an argument: $ARG
done

ARG - можно заменить любой переменной.

Результат вывода.

$ ./args.sh bash is fun
here is an argument: bash
here is an argument: is
here is an argument: fun

Вариант 3 с явным заданием аргументов.

for VAL in 20 3 dog peach 7 vanilla
do
    echo $VAL
done

Значения, указанные в цикле for , также можно генерировать, вызывая другие программы или используя другие функции оболочки:

for VAL in $(ls | grep pdf) {0..5}
do
    echo $VAL
done

$(ls | grep pdf) - вызывает комманду в подоболочке.

{0..5} - определение диапазона, можно с шагом {1..104..2}

Здесь переменная VAL , в свою очередь, будет принимать значение для каждого файла, который командой ls передается в grep и содержит буквы pdf в своем имени (например, doc.pdf или notapdfile.txt ), а затем переменная VAL примет значение каждого числа от 0 до 5.

Функции.

Синтаксис функции в bash следующий:

function myfun ()
{
    # это тело функции
}

Не все эти компоненты обязательны. Вы можете указать или function , или () .

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

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

myfun 2 arb "14 years"

Внутри определения функции аргументы упоминаются так же, как параметры сценария оболочки, то есть как $1 , $2 и т. д.

Это означает, что аргументы «скрывают» параметры, первоначально переданные в сценарий.

Если вы хотите получить доступ к первому параметру скрипта, то перед вызовом функции нужно сохранить $1 в переменной (или передать его в качестве параметра функции).

Если указанная внутри функции команда не объявлена как local , переменные в видимой области являются глобальными. Так же как и переменные тоже глобальные если явно не определены как локальные.

Вызов функции в подоболочке.

RETVAL = $ (myfunc args)

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

$# - выдает количество аргументов, переданных функции

Возвращаемое значение.

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

Шаблон соответствия в BASH. 3 подстановочных символа * ? [].

Шаблоны не являются регулярными выражениями , не путайте их. К примеру точка тут обычный символ.

Шаблоны сопоставляются с файлами в файловой системе.

Когда в командной строке нужно перечислить много файлов, не обязательно вводить имя каждого. Bash обеспечивает сопоставление с шаблоном (иногда называемое подстановкой знаков (wildcarding)), чтобы вы могли указать набор файловс шаблоном.

Самый простой подстановочный знак — символ звездочки ( * ), который будет соответствовать любому количеству любых символов.

Например

*.txt - соответствует любому файлу с расширением txt.

/usr/bin/g* - соответствует всем файлам в /usr/bin , которые начинаются с буквы g

Если при сопоставлении необходимо учитывать *, то его необходимо экранировать знаком .

/tmp/\*.out

Другой специальный символ для сопоставления — вопросительный знак ( ? ) который соответствует одному символу.

Например, source.? будет соответствовать source.c или source.o , но не source.py или source.cpp .

Последний из трех подстановочных символов для сопоставления — квадратные скобки: [ ].

Сопоставление может быть выполнено с любым из символов, перечисленных в квадратных скобках.

Так, шаблон x[abc]y соответствует любому или всем файлам с именами xay , xby или xcy при условии, что они существуют.

Вы можете указать диапазон в квадратных скобках, например: [0–9] для всех цифр.

Если первый символ в скобках — восклицательный знак ( ! ) или «шляпка» ( ^ ), то шаблон определяет все что угодно, кроме оставшихся символов в скобках.

[^aeiou] — любые символам (включая цифры и знаки пунктуации), кроме гласных.

Таблица классов символов.

[:alnum:] Алфавитно-цифровой

[:alpha:] Буквенный

[:ascii:] ASCII (американский стандартный код для обмена информацией)

[:blank:] Пробел и символ табуляции

[:ctrl:] Управляющий символ

[:digit:] Число

[:graph:] Все что угодно, кроме управляющих символов и пробела

[:lower:] Символы в нижнем регистре

[:print:] Все, кроме управляющих символов

[:punct:] Символы пунктуации

[:space:] Пробелы, включая разрывы строк

[:upper:] Символы в верхнем регистре

[:word:] Буквы, цифры и символ подчеркивания

[:xdigit:] Шестнадцатеричный символ

Пример.

*[[:punct:]]jpg

Cоответствует любому имени файла, имеющему любое количество любых символов, за которыми следуют знаки пунктуации, а за ними — буквы jpg.

Таким образом, он будет соответствовать файлам с именами wow!Jpg , some,jpg или photo.jpg , но не файлу this.is.myjpg , потому что прямо перед jpg нет знака пунктуации.

Практика. Поиск файлов, указанного типа.

Создадим скрипт для поиска со следующими параметрами.

Сигнатура:

typesearch.sh [-c dir] [-i] [-R|r] <pattern> <path>

-c Копировать найденные файлы в каталог

-i Игнорировать регистр

-R|r Рекурсивный поиск подкаталогов

- Шаблон типа файла для

- Путь для начала поиска

Сперва определим переменную глубины поиска по умолчанию.

DEEPORNOT="-maxdepth 1"

Анализ аргументов командной строки.

while getopts 'c:irR' opt; do


    case "${opt}" in
        c) # копировать найденные файлы в указанный каталог
            COPY=YES
            DESTDIR="$OPTARG"
            ;;
        i) # игнорировать регистр при поиске
            CASEMATCH='-i'
            ;;
        [Rr]) # рекурсивно
            unset DEEPORNOT;;
        *) # неизвестный/неподдерживаемый вариант
            # при получении ошибки mesg от gretops просто выйти
            exit 2 ;;
    esac    
done

‘c:irR’ - двоеточие указывает на присутствие аргумент для параметра

;; - ограничитель в операторе выбора case

Когда команда getopts анализирует параметр с указанным выше аргументом, она помещает данный параметр в переменную с именем OPTARG.

Мы ее сохраняем в DESTDIR , так как при следующем вызове getopts переменная OPTARG может быть изменена.

Поиск и копирование файлов.

shift $((OPTIND - 1))
PATTERN=${1:-PDF document}
STARTDIR=${2:-.}
# по умолчанию начать здесь 
find $STARTDIR $DEEPORNOT -type f | while read FN
do
    file $FN | egrep -q $CASEMATCH "$PATTERN"
    if (( $? == 0 ))
        # найден один
        then
            echo $FN
        if [[ $COPY ]]
            then
            cp -p $FN $DESTDIR
        fi
    fi
done

Закончив анализ параметров, мы можем избавиться от аргументов, обработанных с помощью shift.

Разовое выполнение shift избавляет лишь от одного аргумента.

В этом случае аргумент, расположенный вторым, становится первым, третий становится вторым и т. д. Указание количества повторений команды shift , например shift5 , позволит избавиться от первых пяти аргументов и в результате аргумент $6 станет $1 , $7 станет $2 и т. д

shift $((OPTIND - 1)) - мы сдвигаем и в начало как $1 и $2 параметр

${1:-PDF document} - при помощи : задаем значение по умолчанию если параметр не задан

-type f - искать только простые файлы (не каталоги)

read - команда, которая читает содержимое строки в переменную

find $STARTDIR $DEEPORNOT -type f | while read FN

Передаем найденные файлы в while и read-ом присваиваем переменной FN