LINUX-BG Адрес : http://www.linux-bg.org |
Кратко ръководство за писане на Bash скриптове |
От: segv Публикувана на: 17-09-2002 Адрес на статията: http://www.linux-bg.org/cgi-bin/y/index.pl?page=article&id=advices&key=344082225 |
Съдържание 1. Увод 2. Hello, World script 3. Променливи 4. Вход от потребителя (user input), оператора <<, аритментични операции 5. Условия (if-then-else, elif, case) 6. Цикли (while, until, for, for-in) 7. Пренасочване и канали 8. Структурата trap 9. Масиви 10. Функции 11. Цветове 12. Използвана литература [--- Увод ---] Всички UNIX системи поддържат голям брой програмни езици, позволяващи на потребителите да си правят свои собсвени програми. Всички дистрибуции имат поддръжка за програмиране за множество командни интерпретатори, езици от по-високо ниво като Perl и Tcl/TK и GUI програмиране за графични среди като KDE и GNOME. Най-често използваните обвивки (shells) са: bash - bourne again shell sh - shell csh - C shell tcsh - Tenex C shell (C shell без поддръжка на tab-completion) zsh - Z shell ksh - Korn Shell Можете да прочетете повече за всяка от тях в техните manual pages. В този tutorial ще се зани- маваме с програмиране за bash. Една програма за bash комбинира UNIX командите по даден начин, за да изпълни определена задача. Програмата за bash представлява текстов файл, съдържащ команди, които можете да въведете със най-обикновен текстов редактор като pico, vi и др. Този текстов файл се нарича скрипт за обвивката. ########################################################### [--- Hello, World script ---] Отворете любимия си текстов редактор и въведете #!/bin/bash echo "Hello, World" за да направите традиционния Hello, World script. Можете да стартирате скрипта като напишете точка (.) пред името на файла. Ако файлът се казва hello, командата би изглеждала така: $ . hello Hello, World $ Другият начин е да направите скрипта изпълним: $ chmod +x hello и да го стартирате с: $ ./hello Hello, World $ По-нататък в tutorial-a ще използваме втория начин. Скриптът hello съдържа два реда. Първия ред казва на системата коя програма да ползва, за да прочете командите във файла, а втория изкарва на екрана Hello, World. Можете да въвеждате коментари в скриптовете си чрез знака "#". #!/bin/bash # Това е коментар echo "Hello, World" # Това също ########################################################### [--- Променливи ---] За да присвоите някаква стойност на променлива трябва да използвате оператора за присвояване (знака за равенство (=)). Въведете името на променливата, знака за равенство и след това стойността й. Пример: num=10 Обърнете внимание, че не трябва да използвате интервали около оператора за присвояване. Ако сложите интервал след него (num= 10), bash ще се опита да изпълни команда "num=" с аргумент 10. Можете да се обърнете към стойността на променлива чрез знака за долар ($). Ето как би изглеждал скриптът Hello, World ако използваме променливи: #!/bin/bash var="Hello, World" echo $var Въпреки че стойностите на променливите могат да бъдат произволни символи, ако включите символи, които се използват от обвивката, възникват проблеми. Тези символи са: интервал, точка (.), знак за долар ($), по-голямо (>) и по-малко (<), (|), (&), (*), ({) и (}). Тези знаци могат да се използват като стойност на променлива ако са поставени в двойни или единични кавички или с обратно наклонени черти. С двойни кавички можете да използвате всички запазени знаци без знака за долар ($). За да го включите в стойността на променливата можете да използвате обратно наклонена черта преди него (\$) или да оградите стойността на променливата с единични кавички. Следващия пример илюстрира всичко описано до сега. #!/bin/bash num1=10 num2=20 msg1="$num1 < $num2 & $num2 > $num1" # Тук ще се покажат всички знаци без знака за долар, # който използваме за да се обърнем към стойността на # променливата. echo $msg1 msg2="\$100 > \$10" # Тук използваме обратно наклонена черта за да покажем знака за долар echo $msg2 msg3='Here we can use all of these symbols: ".", ">", "<", "|", "$", etc' # по този начин всички знаци ще се покажат на екрана, единственият символ, който не може да # се използва е единична кавичка (') echo $msg3 # При всички примери по-горе не е нужно стрингът да се слага в променлива. echo ""\$msg1" is not needed to print '$num1 < $num2 & $num2 > $num1'" Изпълнението на скрипта: $ ./primer1 10 < 20 & 20 > 10 $100 > $10 Here we can use all of these symbols: ".", ">", "<", "|", "$", etc $msg1 is not needed to print '10 < 20 & 20 > 10' $ За да присвоите на някоя променлива резултата от изпълнението на някаква команда, трябва да оградите командата в обратно наклонени апострофи. #!/bin/bash lsvar=`ls ~` echo $lsvar Изпълнението на скрипта: $ ./primer2 books docs ircd movies source work phun stuff $ Ако поставите команда в единични кавички след това можете да използвате името на тази променлива като друго име на командата. #!/bin/bash lssrc='ls ~/source/c' $lssrc Изпълнението на скрипта: $ ./primer3 me0w.c m00.c $ За да използвате резултата от изпълнението на команда в стринг или променлива трябва да заградите командата в скоби и да поставите знака за долар пред нея ($(ls)). #!/bin/bash echo "The date is $(date)" Изпълнението на скрипта: $ ./primer4 The date is пн юни 10 20:36:49 UTC 2002 $ В bash има и няколко специални променливи, които се използват за аргументите на скрипта за обвивката. $0 - име на команда $1 до $9 - аргументите на скрипта $* - всички аргументи от командния ред $@ - всички аргументи от командния ред поотделно $# - броят на аргументите от командния ред Ще разберете разликата между $* и $@ след като прочетете за контролната структура for-in. Погледнете долния пример за по-голяма яснота за специалните променливи. #!/bin/bash echo "The first argument is $1, the second $2" echo "All arguments you entered: $*" echo "There are $# arguments" Изпълнението на скрипта: $ ./primer5 arg1 arg2 arg3 arg4 The first argument is arg1, the second arg2 All arguments you entered: arg1 arg2 arg3 arg4 There are 4 arguments $ Ако изпълните друг скрипт от текущо изпълняващият се, текущият скрипт спира изпълненитето си и предава контрола на другия. След изпълнението му се продължава първия скрипт. При този случай всички променливи, дефинирани в първия скрипт не могат да се използват във втория. Но ако експортирате променливите от първия те ще могат да се използват във втория. Това става с командата export. #!/bin/bash # Това е първият файл. var=100 export var ./primer6-a # EOF #!/bin/bash # Вторият файл. echo "The value of var is $var" # EOF Изпълнението на скрипта (обърнете внимание че вторият файл трябва да е primer6-a, ако използвате друго име, сменете името на файла в първия скрипт). $ ./primer6 The value of var is 100 $ Друг начин за деклариране на променливи е командата declare. Синтаксисът на тази команда е: declare -тип име-на-променливата Типовете променливи са: -r - readonly -i - integer (цяло число) -а - array (масив) -x - export Пример: #!/bin/bash declare -i var # декларираме променлива от тип integer var=100 echo $var declare -r var2=123.456 # декларираме readonly променлива echo $var2 var2=121.343 # опитваме се да променим стойността на var2 echo $var2 # стойността на var2 ще е все още 123.456 Изпълнението на скрипта: $ ./primer7 100 123.456 bash: var2: readonly variable 123.456 $ Повече информация можете да видите в bash manual pages (man bash). ########################################################### [--- User input, <<, аритметични операции ---] За да четете входни данни от вашия script трябва да използвате командата read. В нея няма нищо трудно, но все пак вижте примера: #!/bin/bash echo -n "Enter a string: " read str echo "String you entered: $str" Изпълнението на скрипта: $ ./primer8 Enter a string: hello String you entered: hello $ С оператора << можете да предавате данни на някоя команда. След него трябва да поставите свой ограничител, който представлява някаква дума (пр. EOF) и след данните трябва да поставите същата дума. Пример: #!/bin/bash cat << EOF sth data line EOF Изпълнението на скрипта: $ ./primer9 sth data line $ Командата let в bash се използва за аритметични операции. С нея можете да сравните две стойности или да извършвате различни операции като събиране и умножение. Тези операции често се използват за управление на контролни структури (например цикъла for, за който ще прочетете по-нататък) или за извършване на изчисления. Синтаксисът на командата е показан по-долу: $ let value1 operator value2 Освен този синтаксис можете да използвате двойни скоби. $ (( value1 operator value2 )) Можете да използвате направо операторите за аритметични операции ако и двете променливи са от един и същи тип (например integer). Пример: #!/bin/bash echo -n "Enter the first number: " read var1 echo -n "Enter the second: " read var2 declare -i var3 echo ---------- echo "$var1 + $var2 = $(( $var1+$var2 ))" # тук използваме двойни скоби, както виждате трябва да сложим $ пред # израза, за да се изчисли let res=$var1*var2 echo "$var1 * $var2 = $res" var3=100 var3=$var3+10 echo "$var3" # тъй като тази променлива е декларирана като integer не е нужно да използваме командата let Изпълнението на скрипта: $ ./primer10 Enter the first number: 10 Enter the second: 3 ---------- 10 + 3 = 13 10 * 3 = 30 110 $ Можете да използвате всеки от изброените по-долу оператори с командата let. + - събиране - - изваждане * - умножение / - деление % - остатък при деление > - по-голямо < - по-малко >= - по-голяма или равно <= - по-малко или равно == - равно != - не е равно & - логическо И (AND) | - логическо ИЛИ (OR) ! - логическо НЕ (NOT) За аритметични операции и сравняване можете да използвате командата expr, но тук няма да я описвам. За повече информация вижте нейната документация. За да правите по-точни изчисления използвайте езика bc (http://www.gnu.org/software/bc/bc.html). ########################################################### [--- Условия ---] В тази част ще се запознаем с контролните структури за условия. Много от тях са подобни на структурите в другите езици, но въпреки това има някои разлики. Условията често изискват да се изпълни някаква проверка, която сравнява две стойности. Тази проверка се извършва чрез командата test. Синтаксисът на командата е показан тук: $ test value1 -option value2 $ test string operator string Обърнете внимание, че при сравняване на стрингове се използва оператор, а не опция. Вместо командата test можете да използвате квадратни скоби ( [ и ] ). Командата test $var -eq 1 може да се запише така: $ [ $var -eq 1 ] Резултатът от командата test се запазва в специалната променлива $?. Ако той е true, то $? е равна на нула, ако е false то $? е равна на едно. (В променливата $? всъщност се съхранява изходния код на програмата, която е изпълнена. Ако тя е завършила успешно то той е 0, ако не, той е друго число. При командата test, ако резултатът е true тя спира с изходен код 0, ако е false с 1) Пример: #!/bin/bash var1=10 var2="m000" [ $var1 -eq 10 ] echo $? [ $var2 = "me0w" ] echo $? Изпълнението на скрипта: $ ./primer11 0 1 $ Най-често използваните оператори и опции в test: Сравняване за цели числа Функция -gt (greater than) По-голямо от -lt (less than) По-малко от -ge (greater or equal) По-голямо или равно -le (less or equal) По-малко или равно -eq (equal) Равно -ne (not equal) Неравно Сравняване на стрингове -z Проверява за празен стринг = Равни стрингове != Неравни стрингове Проверки за файлове -f Файлът съществува -s Файлът не е празен -r Файлът може да се чете -w Във файлът може да се записва -x Файлът може да се изпълнява Има много повече опции, но тук няма да ги описвам. Можете да прочетете за тях в test manual pages (man test). Условието if проверява изходното състояние на команда. Ако то е нула командите в структурата се изпълняват. Ако е нещо различно от нула командите в структурата не се изпълняват. Всяка if структура завършва с ключова дума fi и всяка case структура завършва с esac. Контролните структури за условие: if команда then Втората командата се изпълнява ако изходното състояние на първата команда команда е 0. fi if команда then Ако изходното състояние е 0 се изпълнява командата в if, ако е команда някакво друго число се изпълнява командата в else. else команда fi if команда then elif ви позволява да проверявате няколко ситуации в една if структура. команда elif команда then команда else команда fi case стринг in case търси съвпадение на стринга с някои от шаблоните, ако няма такова шаблон) се изпълняват командите по подразбиране (не е задължително да има такива. команда;; *) команда по подразбиране;; esac команда && команда логическо И (AND) команда || команда логическо ИЛИ (OR) !команда логическо НЕ (NOT) По-долу са показани примери с горните условия: #!/bin/bash echo -n "Enter a string: " read str1 echo -n "Enter a string: " read str2 echo -n "Enter a number: " read num1 if [ $str1 == "m000" ]; then # ';' се използва за да може then да е на същия ред echo "str1 = m000" elif [ $str1 == "m000" ] && [ $str2 == "m000" ]; then # логическо И, т.е. echo "str1 and str2 = m000" ще се # изпълни ако и двете условия са true echo "str1 and str2 = m000" else echo "str1 and str2 != m000" fi if [ -f "/etc/passwd" ]; then # пример с файлове cat /etc/passwd fi if [ $num1 -eq 10 ]; then echo "num1 = 10" elif [ $num1 -gt 100 ]; then echo "num1 > 100" else echo "?!?" fi Изпълнението на скрипта: $ ./primer12 Enter a string: m000 Enter a string: m000 Enter a number: 10 str1 = m000 root:x:0:0::/root:/bin/bash bin:x:1:1:bin:/bin: daemon:x:2:2:daemon:/sbin: adm:x:3:4:adm:/var/log: nobody:x:99:99:nobody:/: segv:x:1000:100:,,,:/home/segv:/bin/bash num1 = 10 $ Можете да пробвате скрипта като въвеждате други стойности. Един пример за case: #!/bin/bash echo -n "Enter an option (l, s or al): " read opt case $opt in l) ls -l;; s) ls -s;; al) ls -al;; *) # ако $opt не съвпада с никоя от горните опции ls;; esac Изпълнението на скрипта: $ ./primer13 Enter an option (l, s or al): l total 964 drwxr-xr-x 33 segv users 4096 май 24 19:58 books drwxr-xr-x 5 segv users 4096 юни 12 19:35 docs drwxr-xr-x 7 segv users 4096 май 6 12:34 ircd drwxr-xr-x 2 segv users 4096 май 25 23:22 movies drwxr-xr-x 7 segv users 4096 май 17 11:56 phun drwxr-xr-x 10 segv users 4096 юни 8 15:54 source drwxr-xr-x 2 segv users 4096 юни 1 15:27 stuff drwxr-xr-x 2 segv users 4096 юни 15 08:40 work $ Това е всичко за условията. Можете да пробвате другите оператори и опции на test сами. ########################################################### [--- Цикли ---] Циклите се използват за повтаряне на команди. Контролните структури за цикли са while, until, for и for-in. while и until проверяват резултата на някаква команда докато for и for-in обхождат списък от стойности като присвояват всяка стойност на някаква променлива. Структурите за цикъл са показани по-долу: while команда do while изпълнява дадена команда докато връщаната стойност от команда първата команда е 0 (true). done until команда do until изпълнява дадена команда докато връщаната стойност от команда първата команда е 1 (false). done for променлива in списък от стойности На променливата се присвоява последователно всяка от стойностите do в списъка. команда done for променлива for е предназначен за обръщане към аргументите на скрипта. На do променливата се присвоява последователно всеки от аргументите. команда done while изпълнява команди докато изходната стойност на първата команда е 0. Края на цикъла се указва с ключовата дума done. #!/bin/bash m00=yes while [ $m00 == "yes" ]; do echo -n "Enter a string: " read str1 echo "You entered: $str1" echo -n "Do you want to continue? " read m00 done Скриптът ще се изпълнява докато въвеждате yes, при всяка друга стойност изпълнението спира защото връщаната стойност от [ $m00 == "yes" ] ще е 1 (false). Изпълнението на скрипта е показано тук: $ ./primer14 Enter a string: asd You entered: asd Do you want to continue? yes Enter a string: asd1234123 You entered: asd1234123 Do you want to continue? no $ Един пример с until: #!/bin/bash m00=yes until [ $m00 == "no" ]; do echo -n "Enter a string: " read str1 echo "You entered: $str1" echo -n "Do you want to continue? " read m00 done Този скрипт извършва същата функция като предния, само че се изпълнява докато [ $m00 == "no" ] e false. $ ./primer15 Enter a string: me0w You entered: me0w Do you want to continue? yes Enter a string: zmpf You entered: zmpf Do you want to continue? no $ for-in се използва за обхождане на списък от стойности като всяка от стойностите се присвоява последователно на променлива. #!/bin/bash for cfile in ~/source/c/*.c # това е същото като for cfile in $( ls ~/source/c/*.c ) do echo $cfile done Изпълнението на скрипта: $ ./primer16 /home/segv/source/c/a.c /home/segv/source/c/tmp.c /home/segv/source/c/zmpf.c $ for e същото като for-in, но обхожда аргументите на скрипта. #!/bin/bash for m00 do echo $m00 done Тъй като аргументите на скрипта се съхраняват в специалната променлива $@, горният скрипт може да се направи и с for-in. #!/bin/bash for m00 in $@ do echo $m00 done Изпълнението на скрипта: $ ./primer17 m00 me0w m33p zmpf lkmnp m00 me0w m33p zmpf lkmnp $ Има още няколко команди, които често се използват в циклите. Това са командите true, false и breaк. Командата true има много проста функция - изходната й стойност е винаги 0. false е същата, само че нейната изходна стойност е 1. break се използва за излизане от цикъла. #!/bin/bash declare -i var=0 while true; do # тъй като никога не може условието да стане false цикълът е безкраен echo -n $var var=$var+1 if [ $var -eq 10 ]; then # ако $var е 10 цикълът се прекратява break fi done echo echo bye Можете да направите същия цикъл ако използвате until и false единствената разлика е в реда: while true; do Трябва да е until false; do ########################################################### [--- Пренасочване и канализиране ---] Когато изпълнявате команда, тя изпраща данните на стандартния изход (конзолата). Но можете да пренасочите данните и към файл или примерно към stderr. Също така данните, които въвеждате могат да се вземат от файл. Пренасочването на изходните данни се извършва с знака > след командата. Пример: $ echo m000 > file $ cat file m000 $ Ако искате да добавите данни към съществуващ файл използвайте >>, защото > изтрива предишното съдържание на файла. #!/bin/bash nums="1 2 3" for num in $nums do echo $num done > nums Горния скрипт ще пренасочи изходните данни в файла nums. $ ./nums $ cat nums 1 2 3 $ Канализирането е подобно на пренасочването, с тази разлика че изходните данни от една команда се предават като входни на друга. Най-прост пример за канализиране е архивирането и дезархивирането с tar и gzip. $ tar cvf - files/ | gzip -9 > files.tar.gz или $ gzip -cd files.tar.gz | tar xvf - Един пример с bash скрипт. #!/bin/bash names="Ivan Koljo Asen Sasho Misho" for name in $names do echo $name done | sort > names Скриптът предава изходните данни от echo $name на командата sort и след това изходните данни от нея се пренасочват във файла names. $ ./names $ cat names Asen Ivan Koljo Misho Sasho $ Както виждате имената са подредени по азбучен ред тъй като изходните данни се предават на командата sort. ########################################################### [--- Структурата trap ---] Друг вид структура е trap. trap изпълнява команда при някакво събитие. Тези събития се наричат сигнали. Тук е даден синтаксисът на командата trap. $ trap 'команди' номер-на-сигнала Един често използван сигнал е сигналът за прекъсване (когато потребителят натисне CTRL+C). #!/bin/bash trap 'echo "Bye"; exit 1' 2 echo "Enter a string: " read m00 echo "nah" Изпълнението на скрипта: $ ./trap-ex Enter a string: ^CBye $ Както виждате след като натиснем CTRL+C се изпълнява echo "Bye" след това exit 1, с което програмата спира изпълнението си. Списък с различни номера на сигнали е показан тук: 0 Край на програмата Програмата завършва 2 Терминално прекъсване CTRL+C 9 kill kill-ване на програмата, не може да се улови с trap 24 Спиране на програмата с CTRL+Z, не може да се улови Можете да видите повече номера на сигнали за kill в kill manual pages. ########################################################### [--- Масиви ---] Масивът може да бъде инициализиран с var[xx]="aa", където xx e някакво число или с командата declare -a променлива. За да се обърнем към стойността на някой от елементите на масива трябва да го заградим във фигурни скоби ('{' и '}'). Пример: #!/bin/bash array[1]=m00 array[34]=me0w array[40]=123 echo "array[1] = ${array[1]}" echo "array[34] = ${array[34]}" echo "array[40] = ${array[40]}" Друг начин за присвояване на стойности на елементите от масив е array=( x y z ... ) След това х ще бъде array[0] (забележете първият елемент е 0 а не 1), y - array[1], z - array[2] и т.н. Един пример, демонстриращ повече възможности на масивите: #!/bin/bash declare -a array # декларираме масив с declare echo -n "Enter some numbers separated by space: " read -a array # присвояваме въведените числа на елементите на масива (забележете опцията на read '-a') elements=${#array[@]} # тук присвояваме на променливата elements броя елементи на масива # ${array[@]} съдържа елементите на масива поотделно. Можете да го използвате за цикъл for-in # например: # for i in ${array[@]; do # echo $i # done # сега ще покажем елементите на масива с цикъл while i=0 while [ $i -lt $elements ]; do echo ${array[$i]} let "i = $i + 1" done echo "bye" Изпълнението на скрипта: $ ./primer18 Enter some numbers separated by space: 1 13 14 88 19 1 13 14 88 19 bye $ Можете да видите много повече за масивите (различни алгоритми за сортиране и др.) в Advanced Bash Scripting (http://www.tldp.org/LDP/abs/html/arrays.html) ########################################################### [--- Функции ---] Както в повече програмни езици и в bash можете да използвате функции за да групирате няколко команди в една. Декларирането на функция става така: име_на_функция { команди } или име_на_функция() { команди } За да извикате функция просто трябва да въведете нейното име. #!/bin/bash func() { echo "Hello" echo "This is function" } info() { echo "host uptime: $( uptime )" echo "date: $( date )" } func info echo "That's all folks :)" Изпълнението на скрипта: $ ./primer19 Hello This is function host uptime: 1:46pm up 5 days, 14:39, 1 user, load average: 0.08, 0.05, 0.04 date: сб юни 15 13:46:23 UTC 2002 That's all folks :) $ ########################################################### [--- Цветове ---] И последната част от tutorial-a са цветовете в bash. За да изкараме цветен текст ще използваме командата printf. Цвета се определя с \e[X;XXm, където Х-овете са числа. Списъка с цветовете е показан по-долу: Черно 0;30 Тъмно сиво 1;30 Синьо 0;34 Светло синьо 1;34 Зелено 0;32 Светло зелено 1;32 Cyan 0;36 Light Cyan 1;36 Червено 0;31 Светло червено 1;31 Лилаво 0;35 Светло лилаво 1;35 Кафяво 0;33 Жълто 1;33 Светло сиво 0;37 Бяло 1;37 Изчиства екрана 2J (thanks zEAL) Пример: #!/bin/bash printf "\e[2J" printf "\e[0;34mblue\n" # \n е за нов ред printf "\e[0;32mgreen\n" printf "\e[0;37mand back to default color ...\n" ########################################################### [--- Използвана литература ---] Linux Programming A Beginner's Guide (Richard Petersen) BASH Programming HOWTO - http://www.tldp.org/HOWTO/Bash-Prog-Intro-HOWTO.html Advanced Bash-Scripting Guide - http://www.tldp.org/LDP/abs/html/ ---------------- segv << Създаване на по-бързо и устойчиво ядро | wireless със prism2.5 linux драйвери и настройки dlinkDWL520 >> |
Авторите на сайта, както и техните сътрудници запазват авторските права върху собствените си материали публикувани тук,
но те са copyleft т.е. могат свободно да бъдат копирани и разпространявани с изискването изрично да се упоменава името на автора,
както и да се публикува на видно място, че те са взети от оригиналния им URL-адрес на този сървър (http://www.linux-bg.org). Авторските права на преводните материали принадлежат на техните автори. Ако с публикуването тук на някакъв материал неволно са нарушени нечии права - след констатирането на този факт материалът ще бъде свален.
All trademarks, logos and copyrights mentioned on this site are the property of their respective owners.
|