Упатство:Програмирање во Bash
За да продолжите со читање на ова упатсво, строго препорачуваме да ги имате основните знаења за Bash школката и основните команди за таа школка. Основно познавање на GNU/Linux како оперативен систем. Пожелно е и некое искуство со програмски јазици. Иако ова упатство не е вовед во програмирање, сепак опфаќа некои основни концепти.
Креирање едноставни скрипти
Традиционалната hello world скрипта
#!/bin/bash echo Hello World
Оваа скрипта е составена од 2 линии код. Првата линија ни објаснува која програма системот да ја користи за да ја стартува датотеката.
Втората линија е единствената акција изведена од скриптата, која го принта текстот 'Hello world' на вашиот терминал. Ако добиете нешто слично како ./hello.sh:Command not found, тогаш најверојатно имате грешка во првата линија
'#!/bin/bash'. Побарајте со 'whereis bash' или погледнете во делот 'Барајќи го bash' за да видите како точно да ја напишете оваа линија код.
Едноставна скрипта за бекап
#!/bin/bash tar -cZf /var/my-backup.tgz /home/me/
Во оваа скрипта наместо испринтана порака на терминалот, ние креираме tar-ball од ’home’ корисничкиот директориум. Оваа скрипта не е баш наменета за употреба, подобра и покорисна бекап скрипта ќе биде презентирана подоцна во овој документ.
Пренасочување
Теорија и краток преглед
Постојат 3 опишувачи на датотеки, stdin, stdout и stderr (std=стандард).
Во основа можете:
- да го пренасочите stdout во некоја датотека
- да го пренасочите stderr во некоја датотека
- да го пренасочите stdout во stderr
- да го пренасочите stderr во stdout
- да ги пренасочите stderr и stdout во некоја датотека
- да ги пренасочите stderr и stdout во stdout
- да ги пренасочите stderr и stdout во stderr
1 претстатува stdout (стандарден излез) и 2 stderr (стандарден излез за грешки).
Мала забелешка околу овие работи: со наредбата less можете да ги видите и двата stdout(кој останува во баферот) и stderr кој ќе биде испечатен на екранот, но избришан доколку се обидете да го прелистувате или прегледувате баферот.
Пример: stdout во датотека
Овој пример ќе предизвика излезот од програмот на екранот да биде запишан во некоја датотека.
ls -l > ls.l.txt
Овде, датотеката со име 'ls.l.txt' ќе биде креирана и ќе биде составена од она што го гледате на екранот која ја куцате и извршувате наредбата 'ls -l'.
stderr во датотека
Овој пример ќе предизвика stderr излезот од програмот да биде запишан во датотека.
grep da * 2> grep-errors.txt
Овде, датотеката со име 'grep-errors.txt' ќе биде креирана и ќе биде составена од stderr делот од излезот при извршувањето на 'grep da *' наредбата.
stdout кон stderr
Овој пример ќе предизвика stderr излезот од програмот да биде запишан во истиот покажувач на датотеки него stdout.
grep da * 1>&2
Овде, stdout делот од наредбата е испратен до stderr, можете да го забележите тоа на најразлични начини.
Пример: stderr кон stdout
Овој пример ќе предизвика stderr излезот од програмот да биде запишан во истиот покажувач на датотеки него stdout.
grep * 2>&1
Овде, stderr делот од наредбата е испратен до stdout, ако ставите pipe до less, ќе видите дека линиите кои нормално 'исчезнуваат'(кои што се запишани во stderr) се зачувани сега (затоа што тие се во stdout).
stderr и stdout до датотека
Овој пример ќе го смести секој излез од програмот во некоја датотека. Ова е згодно понекогаш, бидејќи може да се комбинира со cron, ако сакаме наредбата да помине во апсолутна тишина.
rm -f $(find / -name core) &> /dev/null
Ова (размислувајќи за запишувањето во cron) ќе ја избрише секоја датотека со име 'core' во било кој директориум. Најважно е да бидете сигурни што прави оваа наредба, доколку сакате да го избришете нејзиниот излез.
Протоци (pipes)
Ова поглавје објаснува на практичен начин како да користите протоци и зошто би сакале да ги употребувате баш нив.
Која е улогата на протоци, и зошто би ги користел
протоците дозволуваат да се користи излезот на некоја програма како влез на некоја друга.
Протоци и sed
Ова е најобичен начин на користење на протоците.
# ls -l | sed -e "s/[aeio]/u/g"
Еве што се случува овде: Прво наредбата ls -l се извршува, и нејзиниот излез наместо да биде испринтан на екран, е испратен (преку проток) до sad програмот, кој што принта тоа што мора.
Алтернатива до ls -l*.txt
Најверојатно, ова е потешкиот начин да се направи ls -l*.txt, но овде ќе го користите за да ги илустрираме протоците, а не да решаваме некоја дилема
# ls -l | grep "\.txt$"
Овде, излезот од програмата ls -l е испратен преку grep програмата, кој по редослед ќе ги испринта линиите кои одговараат со "\.txt$".
Променливи ($)
Променливите се користат во сите програмски јазици. Нема типови на податоци. Променливата во беш (bash) може да биде број, карактер или низа од карактери. Нема потреба од декларација на променливата, таа ќе се креира само со доделувањето на вредност на нејзината референца.
Hello World! со променливи
#!/bin/bash STR="Hello World!" echo $STR
Втората линија, креира променлива со име STR и е иницијализирана со стрингот 'Hello World' кадешто вредноста на оваа променлива се надополнува со знакот '$'на почетокот.Важно е дека ако не се стави '$' знакот пред променливата, тогаш излезот од програмата ќе биде друг, сигурно не на оној начин на којшто посакуваме да работи.
Проста скрипта за бекап 2
#!/bin/bash OF=/var/my-backup-$(date +%Y%m%d).tgz tar -cZf $OF /home/me/
Скриптата не запознава со друго нешто. Најпрво од се, треба да бидеме запознаени со декларацијата и иницијализацијата на променливите во вториот ред од кодот. Забележи го изразот '$(date +%Y%m%d)'. Ако ја извршите оваа скрипта ќе забележите дека ги извршува командите внатре во заградите, прикажувајќи го излезот од нив. Забележете дека во оваа скрипта, името на излезната датотека ќе биде различно секој ден врз основа на промената на форматот од date наредбата (+%Y%m%d). Ова можете да го промените со додавање на друг формат.
Локални променливи
Локалните променливи можат да бидат креирани со користење на клучниот збор 'local'
#!/bin/bash HELLO=Hello function hello { local HELLO=World echo $HELLO } echo $HELLO hello echo $HELLO
Овој пример би требало да биде доволен, за да покаже како се користат локалните променливи.
Услови
Условите ни даваат за предност да одлучиме дали да се изврши некоја акција, или не, оваа одлука е земена со евалуирање на изразот.
Малку теорија
Условите имаат многу форми. Најпростата форма е:
if израз then наредба,
каде што наредбата ќе биде извршена само ако условот евалуира во точно пошто е од тип bool.'2<1' е израз кој евалуира во неточно, додека '2>1' евалуира во точно.Условите имаат и други форми како на пример: if израз then наредба1 else наредба2 . Кадешто наредба1 ќе биде извршена само ако изразот евалуира во точно, инаку наредба2 ќе биде извршена. Друга форма на формирање на услови е:
if израз1 then наредба1 else if израз2 then наредба2 else наредба3
Во оваа форма е додаден само „ELSE IF 'израз2' THEN 'наредба2', која прави наредба2 да биде извршена само ако израз2 евалуира во точно.
Околу синтаксата:
Основната форма за 'if' конструкцијата во bash е следнава:
if [израз]; then код ако if 'изразот' е точен. fi
Основен пример за услов
if-then
#!/bin/bash if [ "foo" = "foo" ]; then echo expression evaluated as true fi
Кодот ќе биде извршен ако изразот во големите загради е точен и се најде после клучниот збор 'then' и пред 'fi' којшто означува крај на кодот што се извршил по обработката на условот.
Основен пример за услов 2
if-then-else
#!/bin/bash if [ "foo" = "foo" ]; then echo expression evaluated as true else echo expression evaluated as false fi
Услови со променливи
#!/bin/bash T1="foo" T2="bar" if [ "$T1" = "$T2" ]; then echo expression evaluated as true else echo expression evaluated as false fi
Циклуси
- for циклусот е малку различен од другите програмски јазици. Дозволува итерации преку серии од зборови без употреба на низи.
- while извршува парче код ако контролниот израз е точен, и запира кога изразот ќе евалуира во неточно.
- until е речиси ист со while (додека) циклусот, освен тоа што кодот се извршува додека контролниот израз евалуира во неточно.
Пример
#!/bin/bash for i in $( ls ); do echo item: $i done
Во вториот ред ние декларираме променливата да прими вредности од $(ls).Во третиот ред може да биде и подолг по потреба, или да има повеќе линии пред done. 'done' индицира дека кодот што ја користи вредноста на $i е завршен, и $i може да прими нова вредност.
for циклус во C/Perl стил
#!/bin/bash for i in `seq 1 10`; do echo $i done
Пример за while циклус
#!/bin/bash COUNTER=0 while [ $COUNTER -lt 10 ]; do echo The counter is $COUNTER let COUNTER=COUNTER+1 done
Оваа скрипта ’се такмичи’ со добро познатата (C, Pascal, perl, итн) 'for' структура.
Пример за until циклус
#!/bin/bash COUNTER=20 until [ $COUNTER -lt 10 ]; do echo COUNTER $COUNTER let COUNTER-=1 done
Функции
Како во скоро секој програмски јазик можете да користите функции за групирање на парчињата код на пологичен начин или пак да ја користите „светата“ вештина - рекурзијата. Декларирањето на функцијата е уствари самата содржина на пишаната функција my_func { my code }.
Повикувањето на некоја функција е исто како и повикување не некоја програма само со пишување на нејзиното име.
Пример за функција
#!/bin/bash function quit { exit } function hello { echo Hello! } hello quit echo foo
Редовите од 2-4 ја содржат 'quit' функцијата. Редовите код од 5-7 ја содржат 'hello' функцијата. Ако не сте сигурни уствари што прави самата скрипта пробајте да ја стартувате. Кога ќе ја стартувате скриптата прво нешто што ќе забележите е дека функцијата 'hello' е повикана, второ функцијата 'quit' е повикана, а скриптата никогаш не го извршува 10-от ред :)
Пример за функции со покажувачи
#!/bin/bash function quit { exit } function e { echo $1 } e Hello e World quit echo foo
Оваа скрипта е скоро идентична со претходната. Главната разлика е функцијата 'e'. Оваа функција го принта првиот аргумент што ќе го прими, а аргументите во функциите се третираат исто како аргументите во скриптата.
Кориснички интерфејс
Користење на Select за креирање на менија
#!/bin/bash OPTIONS="Hello Quit" select opt in $OPTIONS; do if [ "$opt" = "Quit" ]; then echo done exit elif [ "$opt" = "Hello" ]; then echo Hello World else clear echo bad option fi donе
ко ја стартувате скриптата ќе го видите сонот на програмерите за креирање текстуални менија. Можете да забележите дека е многу слична на 'for' циклусот, дека наместо циклус за секој 'збор' во $OPTIONS, го наведува корисникот.
Користење на командната линија
#!/bin/bash if [ -z "$1" ]; then echo usage: $0 directory exit fi SRCD=$1 TGTD="/var/backups/" OF=home-$(date +%Y%m%d).tgz tar -cZf $TGTD$OF $SRCD
Овде изразот во првиот услов, ја тестира програмата дали примила аргумент ($1) и ја исклучува ако не примила ништо, покажувајќу му на корисникот мала порака.
Разно
Читање на корисничкиот влез со read
Во многу прилики можеби сакате корисникот да внесе некој влез од тастатура, и тука постојат неколку начини како би го извеле тоа: Еве еден од тие начини:
#!/bin/bash echo Please, enter your name read NAME echo "Hi $NAME!"
На друг начин може да добиеме зголемени вредности со read, овој пример ќе го разјасни тоа (As a variant, you can get multiple values with read, this example may clarify this.
#!/bin/bash echo Please, enter your firstname and lastname read FN LN echo "Hi! $LN, $FN !"
Аритметичко евалуирање
На комадната линија (или школката) пробајте го следново:
echo 1+1
Ако очекувавте да видите '2', тогаш ќе бидете разочарани. Но што ако сакате BASH да евалуира неколку бројки кои ги имате? Тогаш пробајте го следново решение:
echo $((1+1))
Од следново ќе произлезе подобар 'логички' излез. Ова е за евалуирање на аритметички израз. Можете да го постигнете тоа со следново:
echo $[1+1]
Ако е потребно да се користат фракции (делење) или повеќе математика, можете да ја користите 'bc' наредбата за да евалуирате повеќе аритметички изрази. Ако извршите "echo $[3/4]" во командната линија, ќе врати 0, бидејќи bash користи само интеџери (int) кога враќа одговори. Ако извршите"echo 3/4|bc -l", тогаш најверојатно ќе добиете 0.75 на излез.
= Барајќи го bash
Секогаш користите #!/bin/bash, доколку сакате да побарате каде се наоѓа тогаш можете да ја користите програмата 'locate bash' или 'find / -name bash' од root директориумот. Препорачани локации за проверка:
- ls -l /bin/bash
- ls -l /sbin/bash
- ls -l /usr/local/bin/bash
- ls -l /usr/bin/bash
- ls -l /usr/sbin/bash
- ls -l /usr/local/sbin/bash
Исто така можете да се обидете и со which bash.
Земање на повратна вредност од програмата
Во bash, повратната вредност од програмата е складирана во специјална променлива со име $?. Ова ни илустрира како да ја заробиме повратната вредност на програмата, со претпоставка дека директориумот 'dada' не постои.
#!/bin/bash cd /dada &> /dev/null echo rv: $? cd $(pwd) &> /dev/null echo rv: $?
Заробувајќи ги излезните команди
Оваа мала скрипта ќе ги покаже сите табели од сите бази на податоци(претпоставувајќи дека имате MySQL инсталирано на Вашиот систем). Исто така размислете за промена на 'mysql' наредбата да користи соодветно корисничко име и лозинка.
#!/bin/bash DBS=`mysql -uroot -e"show databases"` for b in $DBS ; do mysql -uroot -e"show tables from $b" done
Повеќекратни изворни датотеки?
Повеќе кратни изворни датотеки можете да користите со следната наредба:
__TO-DO__
Табели
Oператори за споредба на знаковни низи
- s1 = s2
- s1 != s2
- s1 < s2
- s1 > s2
- -n s1
- -z s1
- s1 одговара со s2
- s1 не одговара со s2
- __TO-DO__
- __TO-DO__
- s1 не е null (содржана е од еден или повеќе карактери)
- s1 е null
Примери за споредба на знаковни низи
Споредба на две низи:
#!/bin/bash S1='string' S2='String' if [ $S1=$S2 ]; then echo "S1('$S1') is not equal to S2('$S2')" fi if [ $S1=$S1 ]; then echo "S1('$S1') is equal to S1('$S1')" fi
Може да пробате и со [ $1 = $2 ] но ова не добра идеја зашто ако било кое од $S1 или $S2 е празно, ќе добиете грешка при парсирање. Подобро решение е x$1=x$2 or "$1"="$2".
Аритметички операции
+
-
/
% (потсетник)
Аритметички релациски операции
-lt (<)
-gt (>)
-le (<=)
-ge (>=)
-eq (==)
-ne (!=)
C програмерите треба едноставно да го мапираат операторот со неговата одговарачка заграда.
Корисни наредби
Некои од следниве наредби, опфаќаат дел од комплетните програмски јазици. Од сите тие наредби, само основните ќе бидат објаснети. За повеќе детали погледни на упатствата кои доаѓаат со секоја команда.
sed
Sed е не-интерактивен уредувач. Наместо да го движите курсорот на екранот, ја користите скриптата да уредите инструкции во Sed, плус името на датотеката која што треба да се уреди. Sed може да се опише и како филтер. Да погледнеме некои примери:
$sed 's/to_be_replaced/replaced/g' /tmp/dummy
Sed ја заменува низата 'to_be_replaced' со низата 'replaced' и врши вчитување од /tmp/dummy датотеката. Резултатот ќе биде испратен до stdout (конзолата), но исто така може да се додаде и '> capture' на крајот од линијата погоре, со што Sed, излезот ќе го испрати во датотеката 'capture'.
$sed 12, 18d /tmp/dummy
Sed ги покажува сите линии освен 12 и 18. Оригиналната датотека не е пременувана преку оваа наредба
awk
Служи за манипулација со податочни датотеки, текстуално истражување и процесирање
Постојат многу алатки од AWK програмскиот јазик, како на пример GNU's gawk, i 'новиот awk' mawk. Принципот е јасен: AWK скенира шема, и за секоја точна шема, ќе биде извршена некоја акција. Уште еднаш, креираме dummy датотека со следнава содржина:
"test123 test tteesstt" $awk '/test/ {print}' /tmp/dummy
test123 test
Шемата AWK го бара стрингот 'test' и акцијата ќе ја изврши само ако во датотеката најде линија со стрингот 'test' е 'print'?? The pattern AWK looks for is 'test' and the action it performs when it found a line in the file /tmp/dummy with the string 'test' is 'print'.
$awk '/test/ {i=i+1} END {print i}' /tmp/dummy
излез: 3
Кога пребарувате за повеќе шени, тогаш може да се замени текстот меѓу наводниците со '-f file.awk' на тој начин што сите шеми и извршувања ќе бидат зачувани во датотеката 'file.awk'
grep
Печати линии кои одговараат со пребарувачката шема.
Досега сретнавме неколку grep наредби во претходните поглавја, кои ги прикажуваат линиите кои одговараат со пребарувачката шема. Но grep може да прави и повеќе од тоа.
Пример:
$grep "restart" /var/log/messages -c
излез: 12
Стрингот „restart“ беше најден 12 пати во датотеката /var/log/messages
wc
Брои линии, зборови и бајти.
Во тековниот пример ќе видиме дека очекуваниот излез не е тој. Dummy датотеката користена во овој пример, го содржи следниов текст: "bash introduction howto test file"
$wc --words --lines --bytes /tmp/dummy
Излез: 2 5 34 /tmp/dummy
wc не се грижи за редоследот на параметрите. Wc секогаш ги принта нив во стандарден редослед, кој е ист како што е прикажан на примерот
sort
Подредување на од текстуалните датотеки.
Овој пат пример датотеката го содржи следниов текст
"b c a"
$sort /tmp/primer
Еве како изгледа излезот:
a b c
bc
Калкулатор за програмски јазици.
'bc' прима калкулации од командната линија (влез од некоја датотека, само не redirector или цефка), но исто така и од корисничкиот интерфејс. Тековната демонстрација ќе ни покаже некои од тие наредби. Забележете дека bc е стартуван со -q параметар за да се избегне пораката за добредојде :)
$bc -q 1 == 5 0 0.05 == 0.05 1 5 != 5 0 2 ^ 8 256 sqrt(9) 3 while (i != 9) { i = i + 1; print i } 123456789 quit
tput
Иницијализира терминал или прашална терминфо база на податоци.
Мала демонстрација на можностите на tput's:
$ tput cup 10 4
Прашалниот се појавува во (y10,x4).
$ tput reset
Го брише екранот и прашалниот се појавува во(y1,x1). Забелеши дека (y0,x0) е на горниот лев ќош.
$ tput cols
Излез: 80
Го покажува бројот на карактери, возможни во x насоката. Препорачливо е да се запознаете со овие програми доколку можете. Постојат еден тон овакви мали програмчиња кои ви овозможуваат да правите „вистинска магија“ на командната линија :)
Корисни скрипти
Многу едноставна скрипта за резервна копија
#!/bin/bash SRCD="/home/" TGTD="/var/backups/" OF=home-$(date +%Y%m%d).tgz tar -cZf $TGTD$OF $SRCD
Преименувач на датотеки
# check for a suffix rename # the rest of this part is virtually identical to the previous section # please see those notes if [ $1 = s ]; then suffix=$2 ; shift ; shift if [$1 = ]; then echo "no files given" exit 0 fi for file in $* do mv ${file} $file$suffix done exit 0 fi # check for the replacement rename if [ $1 = r ]; then shift # i included this bit as to not damage any files if the user does not specify # anything to be done # just a safety measure if [ $# -lt 3 ] ; then echo "usage: renna r [expression] [replacement] files... " exit 0 fi # remove other information OLD=$1 ; NEW=$2 ; shift ; shift # this for loop iterates through all of the files that we give the program # it does one rename per file given using the program 'sed' # this is a sinple command line program that parses standard input and # replaces a set expression with a give string # here we pass it the file name ( as standard input) and replace the nessesary # text for file in $* do new=`echo ${file} | sed s/${OLD}/${NEW}/g` mv ${file} $new done exit 0 fi # if we have reached here then nothing proper was passed to the program # so we tell the user how to use it echo "usage;" echo " renna p [prefix] files.." echo " renna s [suffix] files.." echo " renna r [expression] [replacement] files.." exit 0
Едноставен преименувач на датотеки
#!/bin/bash # renames.sh # basic file renamer criteria=$1 re_match=$2 replace=$3 for i in $( ls *$criteria* ); do src=$i tgt=$(echo $i | sed -e "s/$re_match/$replace/") mv $src $tgt done
Кога нешто ќе тргне наопаку (дебагирање)
Патоказ за да го повикаш BASH
Корисна работа е, да се вметне оваа линија:
#!/bin/bash -x
На излез ќе се добијат интересни информации.