Упатство:Програмирање во Bash

Од Сподели wiki
Прејди на прегледникот Прејди на пребарувањето

За да продолжите со читање на ова упатсво, строго препорачуваме да ги имате основните знаења за 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=стандард).

Во основа можете:

  1. да го пренасочите stdout во некоја датотека
  2. да го пренасочите stderr во некоја датотека
  3. да го пренасочите stdout во stderr
  4. да го пренасочите stderr во stdout
  5. да ги пренасочите stderr и stdout во некоја датотека
  6. да ги пренасочите stderr и stdout во stdout
  7. да ги пренасочите 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

Циклуси

  1. for циклусот е малку различен од другите програмски јазици. Дозволува итерации преку серии од зборови без употреба на низи.
  2. while извршува парче код ако контролниот израз е точен, и запира кога изразот ќе евалуира во неточно.
  3. 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ператори за споредба на знаковни низи

  1. s1 = s2
  2. s1 != s2
  3. s1 < s2
  4. s1 > s2
  5. -n s1
  6. -z s1
  1. s1 одговара со s2
  2. s1 не одговара со s2
  3. __TO-DO__
  4. __TO-DO__
  5. s1 не е null (содржана е од еден или повеќе карактери)
  6. 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

На излез ќе се добијат интересни информации.