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 - чи існує файл

-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

Відповідає будь-якому імені файлу, що має будь-яку кількість будь-яких символів, за якими йдуть знаки пунктуації, а за ними – літери 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