Bash в прикладах

Основи програмування в bash

Ви, мабуть, поки не уявляєте навіщо потрібно вчитися програмуванню в bash. Ось кілька причин:

Bash вже є у вас в системі

Якщо ви перевірите. то швидше за все виявите, що bash вже запущений у вас в системі. Навіть якщо ви використовуєте інший shell як командного інтерпретатора, bash напевно встановлений, тому що він є стандартною командною оболонкою в Лінуксі. Так як bash вже запущений, виконання скриптів написаних на bash-е є ефективним, тому що вони поділяють частина оперативної пам'яті з вже запущеним процесом bash. Та й навіщо завантажувати ще один інтерпретатор, якщо у вас вже є bash, який справляється зі своєю роботою і робить це добре?

Ви вже використовуєте його

Але bash не просто запущений у вашій системі, ви ще щодня взаємодієте з ним. Він завжди поруч, так що є сенс навчитися справлятися з ним щоб повноцінно використовувати всі його можливості. Після цього ваше спілкування з лінуксом стане набагато веселіше і продуктивніше. Але чому ви повинні вчитися програмувати? Все просто: ви вже думаєте в термінах виконання програм, копіювання файлів і перенаправлення виводу програм. Чи не повинні ви тепер вивчити мову, який дозволить вам будувати з цих простих елементів потужні і заощаджують ваш час конструкції з використанням яких ви вже знайомі? Командна оболонка відкриває перед вами весь потенціал UNIX. А bash це командна оболонка Лінукс. Він пов'язує вас з комп'ютером. Вивчивши bash, ви автоматично збільшите свою продуктивність використання UNIX або Linux - все настільки просто.

Перед початком

Неправильний підхід до вивчення мови bash може збити вас з пантелику. Багато новачків вводять команду man bash щоб прочитати сторінку з довідкою, яка містить тільки короткий технічний опис функціоналу командної оболонки. Інші викликають info bash (щоб подивитися документацію в форматі info), але отримують або ту ж сторінку мануала або трохи більше зручний для користувача матеріал.

Новачки можуть бути трохи розчаровані, дізнавшись що стандартна довідка не може надати всієї інформації і призначена для користувачів уже знайомих з основами програмування на мові shell. В мануалі багато чудово організованою технічної інформації, але для новачків її користь невелика.

Ця серія статей допоможе вам освоїтися в командному рядку. У цьому посібнику описано як використовувати основні синтаксичні конструкції мови shell для написання своїх скриптів. Я постараюся пояснити все простою мовою, щоб ви не тільки зрозуміли як це все працює, але і розібралися як це використовувати. Прочитавши цю серію статей, ви навчитеся писати свої власні скрипти і будете комфортно себе почувати в командному рядку. Після цього ви зможете поповнювати свої знання читаючи (і розуміючи!) Стандартну документацію по bash. Давайте почнемо.

змінні оточення

Змінну в bash-е можна визначити в такий спосіб:

$ Myvar = 'Це моя змінна оточення!'

Ця команда створює змінну з ім'ям "myvar" яка містить рядок "Це моя змінна оточення!". Слід зауважити що: По-перше, поруч зі знаком "=" не повинно бути пробілів; (Спробуйте і ви побачите, що на команду з пробілами інтерпретатор видає помилку). По-друге, якщо значення нашої змінної містить прогалини або знаки табуляції, потрібно укласти його в лапки.

Зауваження: За детальною інформацією про використання лапок в bash зверніться до розділу "QUOTING" на сторінці man bash. Існування спеціальних послідовностей символів, які інтерпретатор замінює іншими значеннями, робить пояснення роботи з рядками в bash занадто складним. Тому ми розповімо тільки про найбільш часто використовуваних способах застосування лапок.

По-третє, звичайно можна використовувати подвійні лапки замість одинарних, але саме в цьому прикладі використання подвійних лапок викличе помилку інтерпретатора, так як значення нашої змінної містить один з тих спеціальних символів (про які сказано в зауваженні вище) - "!", Але всередині одинарних лапок ніякі спеціальні символи не працюють. Символ "!" В bash відповідає за так звану "HISTORY EXPANSION" - роботу з файлом історії командного рядка (подробиці див. В мануалі). На дивлячись на те, що функція роботи з історією команд за допомогою "!" Буває дуже корисною, саме зараз ми хочемо бачити його просто як знак оклику.

Давайте подивимося як можна прочитати значення нашої змінної:

$ Echo $ myvar Це моя змінна оточення!

Ставлячи перед ім'ям змінної знак $. ми повідомляємо інтерпретатора, що потрібно замінити її значенням. Це називається підстановкою змінної (variable substitution / expansion).

Потрібно зауважити, що імена змінних в bash чутливі до регістру. Наприклад, myvar і Myvar - це імена різних змінних.

Але що буде, якщо ми спробуємо зробити так:

$ Echo foo $ myvarbar foo

Ми хотіли вивести на екран напис 'fooЕто моя змінна оточення! Bar', але нічого не вийшло. Що ж сталося? Інтерпретатор не зміг визначити значення який саме змінної потрібно підставити ($ m, $ my, $ myvar, $ myvarbar і т.д.) У таких неоднозначних випадках можна явно вказати bash на ім'я змінної:

$ Echo foo $ bar fooЕто моя змінна оточення! Bar

Як ви бачите, уклавши ім'я потрібної змінної у фігурні дужки, ми явно вказали інтерпретатора де знаходиться змінна, а де простий текст. Вираз $ myvar швидше надрукувати і воно працює в більшості випадків, але $ працюватиме завжди коректно. Обидва цих вираження вказують на одну змінну, але ви повинні використовувати друге (з фігурними дужками), якщо ім'я змінної не відокремлено від навколишнього тексту пробілами або знаками табуляції.

Повернемося до згаданої нами можливості експортувати змінні. Експортована змінна автоматично стає доступна для будь-якого скрипта або програми, запущеної після експортування. Shell-скрипт може прочитати значення змінної за допомогою вбудованих в shell засобів роботи зі змінними оточення, а програми на C - використовуючи функцію getenv (). Ось невеликий приклад коду на мові C, який ви можете надрукувати і скомпілювати. Він допоможе поглянути на змінні оточення з боку мови C:

Збережіть цей код в файл myenv.c. а потім скомпілюйте:

$ Gcc myenv.c -o myenv

Після цього в ващей робочої директорії з'явиться виконуваний файл 'myenv'. Запустіть його, і він виведе вам значення змінної 'EDITOR' (якщо воно присвоєно). Ось що вийшло у мене:

$ ./myenv The editor environment variable is set to (null)

Так як змінна не визначена, наша програма не може отримати до неї доступ. Давайте створимо цю змінну і дамо їй якесь значення:

$ EDITOR = mousepad $ ./myenv The editor environment variable is set to (null)

Так теж не працює. Ми очікували що програма надрукує "mousepad", але результат не змінився. Це сталося через те що ми забули експортувати змінну 'EDITOR'. Цього разу має спрацювати:

$ Export EDITOR $ ./myenv The editor environment variable is set to mousepad

Отже, ми на прикладі переконалися, що сторонній процес (в нашому випадку програма на C) не може отримати доступ до змінної оточення до тих пір поки вона не експортовано. Крім того, ви можете визначити змінну і експортувати її однією командою:

Ця команда виконує ту саму дію, що і двокрокова версія (привласнення значення і експорт). Настав сприятливий час показати як скинути значення змінної оточення (іншими словами - видалити змінну) за допомогою 'unset':

$ Unset EDITOR $ ./myenv The editor environment variable is set to (null)

Як розпарсити рядок?

Розпарсити рядок - значить розділити її на більш короткі складові. Це одна з найчастіших операцій, що зустрічаються при написанні shell-скриптів. Іноді скрипту потрібно визначити ім'я конкретного файлу або директорії, знаючи повний (абсолютний) шлях до нього. На bash це можна зробити за все однією командою:

$ Basename /usr/local/share/doc/foo/foo.txt foo.txt $ basename / usr / home / drobbins drobbins

'Basename' - дуже зручна утиліта для розщеплення рядків на складові. Друга команда - 'dirname' - повертає іншу частину рядка (шлях до директорії де знаходиться файл):

$ Dirname /usr/local/share/doc/foo/foo.txt / usr / local / share / doc / foo $ dirname / usr / home / drobbins / / usr / home

Зауваження. команди 'basename' і 'dirname' не визначають наявність файлу або директорії в файловій системі, а працюють тільки з рядками.

підстановка команд

Дуже корисно знати як привласнити змінної результат виконання будь-якої команди. Зробити це досить просто:

$ MYDIR = $ (dirname /usr/local/share/doc/foo/foo.txt) $ echo $ MYDIR / usr / local / share / doc / foo

Те що ми зробили називається підстановка команд. У першому рядку прикладу ми просто уклали команду в конструкцію $ ().

Зауважимо, що те ж саме можна зробити застосувавши замість конструкції $ () зворотні лапки `` (клавіша клавіатури відразу над клавішею Tab):

$ MYDIR = `dirname / usr / local / share / doc / foo / foo.txt` $ echo $ MYDIR / usr / local / share / doc / foo

У БАШЕЄВ, як ми бачимо, можна зробити одну дію декількома різними способами. Використовуючи підстановку команд, ми можемо помістити будь-яку команду в зворотні лапки або в конструкцію $ () і привласнити її висновок змінної. Дуже корисна річ! Ось приклад як використовувати підстановку серії команд з'єднаних через пайп:

$ MYFILES = $ (ls -1 / etc | grep ^ pa) $ echo $ MYFILES pam.conf pam.d pango papersize passwd passwd-

$ MYFILES = $ (ls $ (dirname foo / bar / oni))

Розщеплення рядків для професіоналів

'Basename' і 'dirname' чудові утиліти, але бувають випадки, коли потрібно провести більш складні операції над рядками ніж маніпуляції з шляхами. Для більш ефективної роботи з рядками можна використовувати вбудовані засоби підстановки значень змінних bash. Раніше ми вже використали підстановку змінних, яка виглядала так: $. Але в bash є і вбудовані засоби маніпуляції з рядками. Подивимося на наступний приклад:

$ MYVAR = foodforthought.jpg $ echo $ rthought.jpg $ echo $ odforthought.jpg

Що ж означає конструкція $ з попереднього прикладу? Ми написали всередині $<> ім'я змінної, потім два знака хеша (або дієза, кому як звичніше) і шаблон ( "* fo"). Bash взяв нашу змінну 'MYVAR', знайшов найбільшу по довжині підрядок рядка 'foodforthought.jpg' (починаючи від початку рядка) яка збіглася з шаблоном "* fo" і видалив її. З першого разу в цих тонкощах складно розібратися. Для того щоб зрозуміти як працює конструкція ##. давайте розглянемо покроково як bash шукає збіг підрядка з шаблоном. Починаємо пошук підрядка збігається з шаблоном "* fo" з початку рядка "foodforthought.jpg". Ось список всіх таких подстрок:

f fo збігається з шаблоном "* fo" foo food foodf foodfo збігається з шаблоном "* fo" foodfor foodfort foodforth foodfortho foodforthou foodforthoug foodforthought foodforthought.j foodforthought.jp foodforthought.jpg

Після перевірки всіх цих варіантів, знайдено два рядки потрапляють під шаблон. Bash вибирає найдовшу з них, а потім видаляє цю подстроку і повертає результат.

Друга форма підстановки змінної показана в прикладі відрізняється від першої тільки наявністю одного знака хеша (#), а не двох. І bash виконує ті ж дії, за винятком того, що видаляє не найдовшу а найкоротшу з збіглися з шаблоном подстрок. Як видно, після видалення найкоротшою подстроки збіглася з шаблоном (fo) у нас залишається рядок "odforthought.jpg".

Це може здатися дуже заплутаним, тому я покажу як можна швидко запам'ятати цю фічу bash. Коли ми шукаємо найдовше збіг, то використовуємо два хеша (##), тому що "##" довше ніж "#". А коли шукаємо найкоротший збіг, використовуємо #. Бачите, не так вже й складно! Стривайте, а як же запам'ятати, що пошук починається від початку рядка? Дуже просто! Зауважимо, що в англійській розкладці клавіатури поєднання Shift-4 дає нам знак $ використовується в bash для підстановки значення змінної. Відразу перед знаком $ на клавіатурі знаходиться # (як би спочатку) і таким чином "#" видаляє символи від початку рядка. Ви захочете дізнатися, як видалити послідовність символів від кінця рядка. Як можна здогадатися, це робиться за допомогою знака (%), що знаходиться на клавіатурі відразу після "$". Ось невеликий приклад видалення закінчення рядка:

$ MYFOO = "chickensoup.tar.gz" $ echo $ chickensoup $ echo $ chickensoup.tar

Як ви бачите, одинарний і подвійний знаки відсотка (% і %%) працюють також як "#" і "##", але видаляють подстроку збіглася з шаблоном від кінця рядка. Запам'ятайте, що можна не використовувати знак "*", якщо ви хочете видалити якусь конкретну закінчення рядка.

У цьому прикладі немає різниці використовувати% або %%, тому що є тільки один збіг. І не забувайте при виборі "#" або "%" дивитися на 3,4 і 5 клавіші клавіатури.

Ми можемо використовувати ще одну форму підстановки значення змінної для виділення підрядка по заданій довжині і позиції початку:

Ця форма дуже зручна. Просто вкажіть розділяючи двокрапкою позицію початку підрядка - перше число і довжину підрядка - друге число.

Застосування розщеплення рядків

Поділу рядків навчилися, давайте тепер напишемо невеликий shell-скрипт. Наш скрипт буде приймати один аргумент - ім'я файлу і якщо цей файл має розширення .tar, скрипт буде повідомляти що це тарбол. (Насправді визначати тип файлу по розширенню не зовсім коректно. Для цих цілей існує команда file. Приклад тільки для демонстрації.) Ось цей скрипт:

#! / Bin / bash if [ "$" = "tar"] then echo "Здається це тарбол." else echo "На перший погляд, це не схоже на тарбол." fi

Перший рядок обов'язково має бути присутня в кожному скрипті. Вона показує шлях до інтерпретатора, який буде виконувати скрипт. Її синтаксис, як видно з прикладу - "#!<путь к интерпретатору>".

Збережіть текст скрипта з прикладу в файл mytar.sh. потім змініть права доступу до нього 'chmod 755 mytar.sh' (це зробить файл виконуваним). І, нарешті, запустіть скрипт з аргументом у вигляді імені файлу, як показано в наступному прикладі:

$ ./mytar.sh thisfile.tar Здається це тарбол. $ ./mytar.sh thatfile.gz На перший погляд, це не схоже на тарбол.

Добре, начебто працює, але не дуже функціональний. Перед тим як вдосконалити наш скрипт, розглянемо конструкцію if. використану в ньому. У квадратних дужках порівнюються два рядки ( "=" - це оператор порівняння в bash). Результат порівняння - булева висловлювання 'false' або 'true' (правда чи брехня). Але давайте подивимося які саме рядки порівнюються. Справа все зрозуміло - рядок "tar". Зліва стоїть розібране нами вище вираз, що видаляє початок рядка в змінної $ 1 за шаблоном "*." (Вся рядок до останньої точки, тому що використовуються два хеша ##). Після цієї операції підстановки залишається тільки частина рядка після останньої точки - розширення іншими словами. Якщо в змінній $ 1 міститься ім'я файлу з розширенням "tar", то результатом порівняння рядків буде Булевой true.

Вам напевно цікаво, чому при перевірці умови ми використовували змінну "$ 1". Все дуже просто: змінна $ 1 містить в собі перший аргумент переданий скрипту ($ 2 - другий аргумент і так далі). З цією функцією розібралися, розглянемо тепер докладніше конструкцію умовного вибору "if".

конструкція if

Як і в багатьох мовах програмування, в bash є умовні конструкції. Вони мають формат, описаний нижче. Будьте уважні: слова "if" та "then" повинні знаходиться на різних рядках. Намагайтеся вирівнювати горизонтально всю конструкцію, включаючи заключний "fi" і все "else". Це робить код набагато зручніше для читання і налагодження. У доповненні до простій формі "if, else" є ще кілька інших форм умовних конструкцій:

if [умова] then дію fi

У наведеному вище прикладі 'дію' виконується тільки якщо 'умова' вірно, в іншому випадку скрипт продовжує виконання інструкцій з рядка йде за "fi".

if [умова] then дію elif [условіе_2] then действіе_2 elif [условіе_3] then. else действіе_x fi

А ця конструкція послідовно перевіряє умови і якщо вони вірні, то виконує відповідну дію. Якщо жодна з умов не вірно, то виконується 'действіе_x' стоїть після 'else' (якщо воно є). Потім триває виконання команд йдуть за цією конструкцією "if, then, else", якщо такі є.

В наступній частині

У цій статті ми розглянули базову функціональність bash. Прийшов час трохи прискоритися і почати писати свої власні shell-скрипти. В наступній частині посібника я розповім про функції, циклах, просторі імен і про інші важливі речі. Після цього ми будемо готові писати щодо складні скрипти. До зустрічі!

Дивись також

Схожі статті