You are on page 1of 193

Machine Translated by Google

Примерно извикване на листинг 4.18 е тук, което вече е добавило трима потребители:

Списък на потребителите
==============

абВГДЕ
123 456 888
777
----------------------------

Въведете 'a', за да добавите нов потребител


Въведете „d“, за да изтриете всички потребители
Въведете 'x', за да излезете от това меню
----------------------------

МАСИВИ В BASH

Масивите са налични в много (всички?) езици за програмиране и те също са налични


в bash. Имайте предвид, че едномерният масив е известен като вектор в математиката, а
двумерният масив се нарича матрица; повечето образци на онлайн код обаче използват
думата „масив“ в скриптовете на обвивката.
Списък 4.19 показва съдържанието на Array1.sh , което илюстрира как да дефинирате
масив и да осъществявате достъп до елементи в масив.

ЛИСТИНГ 4.19: Array1.sh

# инициализиране на масива с имена


names[0]="john"
names[1]="nancy"
names[2]="jane"
names[3]="steve"
names[4]="bob"

# показване на първия и втория запис echo "Първи индекс: $


{names[0]}" echo "Втори индекс: ${names[1]}"

Списък 4.19 дефинира масива с имена , който се инициализира с пет низа, започвайки
от индекс 0 до индекс 4. Двата оператора за ехо показват първия и втория елемент в
масива с имена , които са съответно с индекс 0 и 1. Резултатът от листинг 4.19 е тук:

Първи индекс: john


Втори индекс: Нанси
Machine Translated by Google

Ако имате нужда от достъп до всички елементи в масив, можете да го направите с


някой от следните кодови фрагменти:

${array_name[*]} $
{array_name[@]}

Листинг 4.20 показва съдържанието на шел скрипта loadarray.sh , който инициализира


масив и след това отпечатва съдържанието му.

ЛИСТИНГ 4.20: loadarray.sh

#!/bin/bash
numbers="1 2 3 4 5 6 7 8 9 10" array1=( 'echo "$numbers" ')

total1=0

общо 2=0

за номер в "${array1[@]}" направете #echo

"елемент от масив: $num"


total1+=$num нека
total2+=$num готово

echo "Total1: $total1" echo "Total2: $total2"

Листинг 4.20 дефинира низова променлива numbers , която съдържа цифрите от 1


до 10 включително. Променливата array1 се инициализира с всички стойности на масива
с числа от командата echo , която е вътре в двойка обратни отметки.
След това двете числови променливи total1 и total2 се инициализират на 0,
последвани от for цикъл, който намира сумата от всички числа в променливата array1 .
Последната двойка ехо изрази показва резултатите. Стартирайте скрипта на обвивката в
листинг 4.20. Резултатът е както следва:

Общо1: 012345678910
Общо 2: 55

Както можете да видите, total1 е резултат от добавянето на елементите на масива с


числа в един низ, докато total2 е числовата сума на елементите на масива с числа .
Разликата се дължи на ключовата дума let в цикъла.
Machine Translated by Google

Листинг 4.21 показва съдържанието на шел скрипта update-array.sh, който


ви показва някои операции, които можете да извършите върху инициализиран масив.

ЛИСТИНГ 4.21: update-array.sh

array=("аз" "обичам" "дълбоко" "ястие" "пица")

#първият елемент от масива: echo ${array[0]}

#всички елементи на масив: echo


${array[@]}

#всички индекси на масив: echo


${!array[@]}

#Премахване на елемент от масив при индекс 3: unset


array[3]

#добавете нов елемент от масив с индекс 1234: array[1234]="в Чикаго"

#всички елементи на масив: echo


${array[@]}

Стартирайте кода в листинг 4.21 и ще видите следния резултат:

аз

Обичам дълбока пица 0 1 2 3 4

Обичам дълбока пица в Чикаго

РАБОТА С МАСИВИ

Масивите ви позволяват да „групирате заедно“ свързани елементи от данни


като редове и след това всеки ред съдържа логически свързани стойности на
данни. Като прост пример, следният масив дефинира три полета за клиент
(очевидно не е пълен набор от полета):

cust[0] = име cust[1] =


Адрес cust[2] = телефонен
номер
Machine Translated by Google

Записите на клиентите могат да бъдат записани в текстов файл, който може да


бъде прочетен по-късно от shell скрипт. Ако не сте запознати с текстовите файлове,
има CSV (стойности, разделени със запетая) и TSV (стойности, разделени с табулатори),
както и файлове, които имат различни разделители, като двоеточие (:) и вертикална
черта ( |) символ. Разделителят се нарича IFS (Internal Field Separator).
Този раздел съдържа няколко шел скрипта, които илюстрират някои полезни
функции на масивите в bash. Списък 4.22 показва съдържанието на fruits-array1.sh ,
което илюстрира как да използвате масив и някои операции, които можете да
извършвате върху масиви.
Синтаксисът в bash е достатъчно различен от другите езици за програмиране
че си струва да видите няколко примера, за да изследвате поведението му.

ЛИСТИНГ 4.22: fruits-array1.sh

#!/bin/bash

# метод №1:
плодове[0]="ябълка"
плодове[1]="банан"
плодове[2]="череша"
плодове[3]="портокал"
плодове[4]="круша" ехо
"първи плод: ${fruits[0]}"

# метод #2:
декларирайте -a fruits2=(ябълка банан череша портокал круша) echo "първи плод: $
{fruits2[0]}"

# диапазон от елементи: echo


"последните две: ${fruits[@]:3:2}"

# подниз на елемент: echo "подниз: $


{fruits[1]:0:3}"

arrlength=${#fruits[@]} echo "дължина:


${#fruits[@]}"

Стартирайте кода в листинг 4.22 и ще видите следния резултат:

първи плод: ябълка първи


плод: ябълка последни две:
портокал круша подниз: дължина
на забрана: 5
Machine Translated by Google

Списък 4.23 показва съдържанието на names.txt, а листинг 4.24 показва


съдържанието на array-from-file.sh , който съдържа for цикъл за итерация през
елементите на масив, чиито начални стойности са базирани на съдържанието на
names.txt.

ОБЯВКА 4.23: names.txt

Джейн Смит
Джон Джоунс
Дейв Едуардс

ЛИСТИНГ 4.24: array-from-file.sh

#!/bin/bash

names="names.txt" contents1=( `cat


"$names"` )

echo "Първи цикъл:" за w в "$


{contents1[@]}" do echo "$w" done

IFS=""
names="names.txt" contents1=( `cat
"$names"` )

echo "Втори цикъл:" за w в "$


{contents1[@]}" do echo "$w" done

Листинг 4.24 инициализира променливата contents1 на масива чрез


заместване на команда с командата cat , последвана от for цикъл, който
показва елементи от contents1 на масива . Вторият for цикъл е същият код
като първия for цикъл, но този път със стойността на IFS , равна на „”, което
има ефект на указване на новия ред като разделител. Следователно вторият
цикъл показва две стойности на данни на ред, което отразява съдържанието
и същото оформление names.txt.
Стартирайте кода в листинг 4.24 и ще видите следния резултат:

Първи цикъл:
Machine Translated by Google

Джейн
Смит
Джон
Джоунс
Дейв
Едуардс

Втори цикъл:
Джейн Смит
Джон Джоунс
Дейв Едуардс

Списък 4.25 показва съдържанието на array-function.sh , което илюстрира как да


инициализирате масив и след това да покажете съдържанието му в дефинирана от
потребителя функция.

ЛИСТИНГ 4.25: array-function.sh

#!/bin/bash

# компактна версия на кода по-късно в този скрипт: #items() { за ред в "${@}" ; do printf "%s\n" "$
{line}" ; Свършен ; } #aa=( 7 -4 -e ); елементи „${aa[@]}“

items() { for line


in "${@}" do printf "%s\n" "${line}"

done

arr=( 123 -abc 'моите данни' ) елементи "${arr[@]}"

Листинг 4.25 съдържа функцията items() , която показва съдържанието на масива arr ,
който е инициализиран преди извикването на тази функция. Резултатът е показан тук:

123
-abc

моите данни

Списък 4.26 показва съдържанието на array-loops1.sh , което илюстрира как да се


определи дължината на инициализиран масив и след това да се покаже съдържанието му
чрез for цикъл.
Machine Translated by Google

ЛИСТИНГ 4.26: array-loops1.sh

#!/bin/bash

плодове[0]="ябълка"
плодове[1]="банан"
плодове[2]="череша"
плодове[3]="портокал"
плодове[4]="круша"

# дължина на масива:
arrlength=${#fruits[@]} echo
"дължина: ${#fruits[@]}"

# печат на всеки елемент чрез цикъл: for (( i=1; i<${arrlength}


+1; i++ )); do echo "елемент $i от ${arrlength} : готово

" ${плодове[$i-1]}

Листинг 4.26 съдържа код за инициализиране на масив и показване на неговите


стойности. Стартирайте кода в листинг 4.26 и ще видите следния резултат:

дължина: 5
елемент 1 от 5: елемент ябълка 2 от 5:
елемент банан 3 от 5: елемент череша 4 от

5: елемент портокал 5 от 5: елемент круша

РЕЗЮМЕ

Тази глава започна с примери за аритметични операции и операторите, които са


налични за извършването им. След това научихте как да присвоявате стойности на
променливи и след това как да четете въведеното от потребителя в шел скрипт.
След това видяхте как да използвате командата test за променливи, файлове и
директории (като например определяне дали две променливи са равни). Освен това
видяхте примери за различни релационни, булеви и низови оператори, които са
налични в bash.
След това научихте за условната логика (if/elif/fi), оператора case/esac , заедно с
циклите for , вложените цикли и циклите while . Освен това видяхте как да създавате
свои собствени функции в шел скриптове. И накрая, вие
Machine Translated by Google

научихте как да работите с масиви в bash, като например инициализиране на масиви и


актуализиране на тяхното съдържание във for цикли.
Machine Translated by Google

ГЛАВА 5

ФИЛТРИРАНЕ НА ДАННИ С grep

T
неговата глава ви въвежда в многостранния grep команда, чиято цел
е да вземете поток от текстови данни и да го намалите само до частите, които ви интересуват
относно. Командата
grep е полезна не само сама по себе си, но и в
връзка с други команди, особено командата find . Това
глава съдържа много примери за кратки кодове, които илюстрират различни опции на
командата.
grep Някои примерни кодове илюстрират как да комбинирате командата grep
с команди от предишни глави.
Първата част на тази глава представя командата, използвана в
grep
изолация, комбинирана с метасимволите на регулярния израз (от
Глава 1), а също и с кодови фрагменти, които илюстрират как да използвате някои от
опции на командата.
grepСлед това ще научите как да съпоставяте диапазони от
редове и как да използвате обратните препратки и метасимволите „escape“ в
grep.
Втората част на тази глава ви показва как да използвате grep за команда

намиране на празни редове и общи редове в набори от данни, както и използването на ключове

за съпоставяне на редове в набори от данни. Ще научите как да използвате класове за символи с

командата grep , както и обратната наклонена черта „\“ и как да се посочи


множество съвпадащи модели. След това ще научите как да комбинирате grep
командата с командата find и командата xargs , което е полезно
за съвпадение на шаблон във файлове, които се намират в различни директории. Този раздел
също така съдържа някои примери за често срещани грешки, които хората правят с
grep команда.
Machine Translated by Google

Третият раздел обсъжда накратко егреп команда и fgrep


команда, които са свързани команди, които предоставят допълнителна функционалност
който не е наличен в стандартната помощна програма grep . Последният раздел съдържа a
случай на употреба, който илюстрира как да използвате командата
grep за намиране на съвпадащи редове

които след това се обединяват, за да се създаде нов набор от данни.

КАКВО Е КОМАНДАТА GREP?

Командата grep („Отпечатване на глобален регулярен израз“) е полезна за


намиране на поднизове в един или повече файлове. Няколко примера са тук:


grep abc *sh показва всички редове на abc във файлове със суфикс sh.

grep -i abc *sh е същата като предходната заявка, но регистър
нечувствителен.

grep -l abc *sh показва всички имена на файлове със суфикс sh , които съдържат
абв.

grep -n abc *sh показва всички номера на редове на срещанията на
низ abc във файлове със суфикс sh.

Можете да извършвате логически И и логически ИЛИ операции с този синтаксис:


grep abc *sh | grep def съвпада с редове, съдържащи abc И def.

grep "abc\|def" *sh съвпада с редове, съдържащи abc ИЛИ def.

Можете също така да комбинирате превключватели: следната команда показва


имената на файловете, които съдържат низа abc (без значение за регистъра на буквите):

grep -il abc *sh

С други думи, предходната команда съвпада с имена на файлове, които съдържат


abc, Abc, ABc, ABC, abC и така нататък.
Друг (по-малко ефективен начин) за показване на редовете, съдържащи abc (case
нечувствителен) е тук:

cat file1 |grep -i abc

Предходната команда включва два процеса, докато „grep


използване на превключвател -l вместо cat за въвеждане на желаните от вас файлове” включва подход
единичен процес. Времето за изпълнение е приблизително същото за малки текстови файлове,
Machine Translated by Google

но времето за изпълнение може да стане по-значимо, ако работите с


множество големи текстови файлове.

Можете да комбинирате командата за сортиране , символа за тръба и grep

командата. Например, следната команда показва файловете с Jan


дата в нарастващ размер:

ls -l |grep " януари " | сортиране -n

Примерен изход от предходната команда е тук:

-rw-r--r-- 1 oswaldcampesato2 персонал -rw-r--r-- 1 oswaldcampesato2 3 27 септември 2013 г. abc.txt


персонал control1.txt 6 21 септември 2013 г

-rw-r--r-- 1 oswaldcampesato2 персонал fiblist.txt 27 28 септември 2013 г

-rw-r--r-- 1 персонал oswaldcampesato2 -rw-r--r-- 1 персонал 28 14 септември 2013 дестинация


oswaldcampesato2 -rw-r--r-- 1 персонал oswaldcampesato2 Divisors.py 36 14 септември 2013 източник
195 28 септември 2013 г

-rw-r--r-- 1 oswaldcampesato2 персонал Divisors2.py 267 28 септември 2013 г

МЕТАХАРАКТЕРИ И КОМАНДАТА GREP

Основните градивни елементи са регулярните изрази, които съответстват на a


единичен знак. Повечето знаци, включително всички букви и цифри, са нормални
изрази, които съвпадат сами по себе си. Всеки метазнак със специално значение
може да бъде цитиран, като пред него поставите обратна наклонена черта.

Регулярният израз може да бъде последван от едно или няколко повторения


оператори:

• „.“ съвпада с всеки отделен знак.


• "?" показва, че предходният елемент е незадължителен и ще бъде съпоставен при
най-много веднъж: Z? съответства на Z или ZZ.

• “*” показва, че предходният елемент ще бъде съпоставен с нула или повече


пъти: Z* съвпада с Z, ZZ, ZZZ и така нататък.
• “+” показва, че предходният елемент ще бъде съпоставен с един или повече
пъти: Z+ съответства на ZZ, ZZZ и така нататък.
• “{n}” показва, че предходният елемент е съвпаднал точно n пъти: Z{3}
съответства на ZZZ.
Machine Translated by Google

• “{n,}” показва, че предходният елемент е съпоставен n или повече пъти: Z{3}


съвпада със ZZZ, ZZZZ и т.н. • “{,m}” показва,
че предходният елемент е съпоставен най-много m пъти: Z{,3} съвпада с Z, ZZ и
ZZZ. • “{n,m}” показва, че
предходният елемент е съпоставен поне n пъти, но не повече от m пъти: Z{2,4}
съвпада със ZZ, ZZZ и ZZZZ.

Празният регулярен израз съответства на празния низ (т.е. ред във входния поток
без данни). Два регулярни израза могат да бъдат съединени от инфикс оператора „|.“
Когато се използва по този начин, операторът на инфикс се държи точно като
логически израз „ИЛИ“, който насочва командата да върне всеки ред,
grepкойто съответства

на някой от регулярните изрази.

ИЗБЯГВАНЕ НА МЕТАХАРАКТЕРИ С КОМАНДАТА GREP

Листинг 5.1 показва съдържанието на lines.txt , който съдържа редове с


думи и метасимволи.

ОБЯВКА 5.1: lines.txt

abcd
аб
абв
cd

defg .*.

..

Следната команда grep изброява редовете с дължина 2 (използвайки операторите


^ („започнете с“) и $ („завършете с“) за ограничаване на дължината) в lines.txt:

grep '^..$' lines.txt

Следващата команда изброява редовете с дължина две в lines.txt , които съдържат две
точки (обратната наклонена черта казва на grep да интерпретира точките като действителни
точки, а не като метазнаци):

grep '^\.\.$' lines.txt

Резултатът е показан тук:


Machine Translated by Google

аб
cd
..

Следващата команда също показва редове с дължина две, които започват и


завършват с точка ( * съвпада с всеки текст с произволна дължина, включително никакъв
текст, и се използва като метасимвол, защото не е предшестван от обратна наклонена черта):

grep '^\.*\.$' lines.txt

Следващата команда изброява редовете, които съдържат точка, последвана от


звездичка и след това друга точка (* вече е знак, който трябва да бъде съпоставен,
защото е предшестван от обратна наклонена черта):

grep '^\.\*\.$' lines.txt

ПОЛЕЗНИ ОПЦИИ ЗА КОМАНДАТА GREP

Има много видове възможности за съвпадение на шаблони с командата и grep

този раздел съдържа еклектична комбинация от такива команди, които обработват общи
сценарии.
В следващите примери имаме четири текстови файла (два .sh и два .txt) и два
документа на Word в директория. Низът abc се намира на един ред в abc1.txt и на три
реда в abc3.sh. Низът ABC се намира на два реда в ABC2.txt и на четири реда в ABC4.sh.
Забележете, че abc не се намира в ABC файлове и ABC не се намира в abc файлове.

ls *

ABC.doc ABC4.sh abc1.txt ABC2.txt


abc.doc abc3.sh

Следният кодов фрагмент търси срещания на низа abc във всички файлове в
текущата директория, които имат sh като суфикс:

grep abc *sh abc3.sh:abc


в началото
abc3.sh: завършва с -abc abc3.sh: abc е в
средата
Machine Translated by Google

Опцията -c отчита броя на срещанията на низ (обърнете внимание, че даже


въпреки че ABC4.sh няма съвпадения, той все още ги брои и връща нула):

grep -c abc *sh

Резултатът от предходната команда е тук:

ABC4.sh:0
abc3.sh:3

Опцията -e ви позволява да съответствате на модели, които иначе биха причинили синтактични


проблеми (символът „-“ обикновено се интерпретира като аргумент за grep):

grep -e "-abc" *sh abc3.sh:завършва


с -abc

Опцията -e също ви позволява да съпоставите множество шаблони.

grep -e "-abc" -e "коментар" *sh

ABC4.sh:# ABC в коментар abc3.sh:завършва с -abc

Опцията -i извършва съпоставяне без значение на малки и големи букви:

grep -i abc *sh ABC4.sh:ABC в


началото
ABC4.sh: завършва с ABC
ABC4.sh:ABC е в средата ABC4.sh:# ABC в коментар abc3.sh:abc
в началото

abc3.sh: завършва с -abc


abc3.sh: abc е в средата

Опцията -v "обръща" съвпадащия низ, което означава, че изходът се състои от редовете, които
не съдържат посочения низ (ABC не съвпада, защото -i не се използва и ABC4.sh има изцяло празен
ред):

grep -v abc *sh

Използвайте опцията -iv , за да покажете редовете, които не съдържат указано


низ, използващ съвпадение без значение за главни и малки букви:
Machine Translated by Google

grep -iv abc *sh ABC4.sh:


abc3.sh: този
ред няма да съвпада

Опцията -l е да изброява само имената на файлове, които съдържат успешно съвпадение


(обърнете внимание, че това съответства на съдържанието на файловете, а не на имената на
файловете). Документът на Word съвпада, защото действителният текст все още се вижда от
grep, той просто е заобиколен от безсмислици за собствено форматиране. Можете да правите
подобни неща с други формати, които съдържат текст, като XML, HTML и .csv:

grep -l abc *

abc1.txt abc3.sh
abc.doc

Опцията -l е да изброява само имената на файлове, които съдържат успешно съвпадение:

grep -l abc *sh

Използвайте опцията -il , за да покажете имената на файловете, които съдържат определен низ, като използвате

съвпадение без значение за главни и малки букви:

grep -il abc *doc

Предходната команда е полезна, когато искате да проверите за появата на низ в документи


на Word.
Опцията -n указва номерата на редовете на всеки съответстващ файл:

grep -n abc *sh abc3.sh:1:abc


в началото
abc3.sh:2:завършва с -abc abc3.sh:3:abc е в
средата

Опцията -h потиска показването на името на файла за успешно съвпадение:

grep -h abc *sh abc в началото

завършва с -abc
abc е в средата
Machine Translated by Google

За следващата поредица от примери използваме columns4.txt, както е показано в


листинг 5.2.

ОБЯВКА 5.2: columns4.txt

123 ЕДНО ДВЕ


456 три четири
ЕДНО ДВЕ ТРИ ЧЕТИРИ пет 123
шест
едно две три
четири пет

Опцията -o показва само съответстващия низ (по този начин избягвате


връщане на целия ред, който съвпада):

grep -o една колона4.txt

Опцията -o , последвана от опцията -b , показва позицията на съвпадащия низ (връща


позицията на знака, а не номера на реда. „o“ в „едно“ е 59-ият знак от файла):

grep -o -b една колона4.txt

Можете да зададете рекурсивно търсене, както е показано тук (изходът не се показва,


защото ще бъде различен за всеки клиент или акаунт. Това търси не само всеки файл в
директория /etc, но всеки файл във всяка поддиректория на etc):

grep -r abc /и т.н

Предходните команди съответстват на редове, където посоченият низ е подниз на по-дълъг низ във файла. Например,

предходните команди ще съпоставят срещанията на abc , както и abcd, dabc, abcde и т.н.

grep ABC *txt

ABC2.txt:ABC в началото или ABC в средата или в края на ABC


ABC2.txt:ABCD DABC

Ако искате да изключите всичко с изключение на точно съвпадение, можете да използвате


опцията -w , както е показано тук:

grep -w ABC *txt


Machine Translated by Google

ABC2.txt:ABC в началото или ABC в средата или в края на ABC

Ключът --color показва съвпадащия низ в цвят:

grep --color abc *sh abc3.sh:abc в


началото abc3.sh:завършва с -abc

abc3.sh: abc е в средата

Можете да използвате двойката метасимволи .*, за да намерите срещанията на два


думи, които са разделени с произволен брой междинни знаци.
Следващата команда намира всички редове, които съдържат низовете one and
три с произволен брой междинни знаци:

grep "one.*three" columns4.txt one two three

Можете да "обърнете" предходния резултат, като използвате ключа -v , както е


показано тук:

grep -v "едно.*три" колони4.txt 123 ЕДНО ДВЕ

456 три четири


ЕДНО ДВЕ ТРИ ЧЕТИРИ пет 123 шест

четири пет

Следващата команда намира всички редове, които съдържат низовете едно и три с
произволен брой междинни знаци, където съвпадението включва сравнение без значение
за главни и малки букви:

grep -i "едно.*три" columns4.txt ЕДНО ДВЕ ТРИ ЧЕТИРИ

едно две три

Можете да "обърнете" предходния резултат, като използвате ключа -v , както е


показано тук:

grep -iv "едно.*три" колони4.txt 123 ЕДНО ДВЕ 456 три четири

пет 123 шест


четири пет
Machine Translated by Google

Понякога трябва да търсите във файл за наличието на един от два низа. Например,
следната команда намира файловете, които съдържат
начало или край:

grep -l 'начало\|край' * ABC2.txt ABC4.sh

abc3.sh

По-късно в главата ще видите как да намерите файлове, които съдържат чифт


низове чрез командите
grep и. xargs

Класове символи и командата grep

Този раздел съдържа някои прости команди от един ред, които комбинират
grep командата с класове знаци.

ехо "abc" | grep '[:alpha:]' abc

ехо "123" | grep '[:alpha:]' (не връща нищо, няма


съвпадение) echo "abc123" | grep '[:alpha:]' abc123

ехо "abc" | grep '[:alnum:]' abc echo "123" | grep

'[:alnum:]' (не връща нищо, няма съвпадение) echo


"abc123" | grep '[:alnum:]' abc123 echo "123" | grep
'[:alnum:]' (не връща нищо, няма съвпадение) echo "abc123"
| grep

'[:alnum:]' abc123 echo "abc" | grep '[0-9]' (не връща


нищо, няма съвпадение) echo "123" | grep '[0-9]'
123 ехо "abc123" | grep '[0-9]' abc123

ехо "abc123" | grep -w '[0-9]' (не връща нищо, няма


съвпадение)

РАБОТА С ОПЦИЯТА -c В grep


Machine Translated by Google

Помислете за сценарий, при който директория (като например директория с регистрационни


файлове) съдържа файлове, създадени от външна програма. Вашата задача е да напишете шел
скрипт, който определя кой (ако има такъв) от файловете, които съдържат две срещания на низ,
след което се извършва допълнителна обработка на съответстващите файлове (напр. използвайте
имейл, за да изпратите регистрационни файлове, съдържащи две или повече грешки съобщения
до системен администратор за разследване).
Едно решение включва опцията -c за grep, последвано от допълнителни
извиквания на grep
команда.

Командните фрагменти в този раздел предполагат следните файлове с данни


чието съдържание е показано по-долу.

Файлът hello1.txt съдържа следното:

здравей свят1

Файлът hello2.txt съдържа следното:

здравей свят2 здравей


свят2 втори път

Файлът hello3.txt съдържа следното:

здравей свят3
здравей свят3 две здравей
свят3 три

Стартирайте следните команди: (2>/dev/null поддържа предупреждения и


грешки, причинени от празни директории от претрупване на изхода):

grep -c hello hello*txt 2>/dev/null hello1.txt:1

здравей2.txt:2
hello3.txt:3 grep -l hello

hello*txt 2>/dev/null hello1.txt

hello2.txt
hello3.txt grep -c

hello hello*txt 2>/dev/null |grep ":2$" hello2.txt:2

Обърнете внимание как използваме метасимвола „завършва с“ $ , за да вземем само


файловете, които имат точно две съвпадения. Ние също използваме двоеточие (":2$"), а не просто
Machine Translated by Google

„2$“ предотвратява прихващането на файлове, които имат 12, 32 или 142 съвпадения
(които завършват на :12, :32 и :142).
Какво ще стане, ако искаме да покажем „две или повече“ (както в „2 или повече грешки в
регистрационния файл“)? Вместо това бихте използвали командата за обръщане (-v) , за да
изключите броя точно 0 или точно 1.

grep -c hello hello*txt 2>/dev/null |grep -v ':[0-1]$' hello2.txt:2

здравей3.txt:3

В приложение от реалния свят бихте искали да премахнете всичко след двоеточието, за да


върнете само имената на файловете. Има много начини да направите това, но ние ще използваме
командата cut , която научихме в глава 1, която включва дефиниране на : като разделител с -d":"
и използване на -f1 за връщане на първата колона (т.е. частта преди двоеточие в върнатия текст):

grep -c здравей здравей*txt 2>/dev/null | grep -v ':[0-1]$'| изрежете -d":" -f1

hello2.txt
hello3.txt

СЪОТВЕТСТВИЕ НА ДИАПАЗОН ОТ ЛИНИИ

В Глава 1 видяхте как да използвате командите head и tail за показване на диапазон от


редове в текстов файл. Сега да предположим, че искате да търсите в диапазон от редове за низ.
Например следната команда показва редове от 9 до 15 на longfile.txt:

cat -n longfile.txt |глава -15|опашка -9

Резултатът е тук:

7 и всеки ред 8 съдържа

9 едно или
още 10 думи 11 и ако 12

използвате котката

13 команда на
14 съдържание на файла 15
превъртане
Machine Translated by Google

Тази команда показва подмножеството от редове от 9 до 15 на longfile.txt


които съдържат низа и:

cat -n longfile.txt |глава -15|опашка -9 | grep и

Резултатът е тук:

7 и всеки ред

11 и ако 13 командвате

Тази команда включва интервал след думата и по този начин изключва реда с
думата команда:

cat -n longfile.txt |глава -15|опашка -9 | grep "и"

Резултатът е тук:

7 и всеки ред

11 и ако вие

Обърнете внимание, че предходната команда изключва редове, които завършват


на и , защото нямат интервал след и в края на реда. Можете да поправите тази ситуация
с оператор „ИЛИ“, включително и в двата случая:

cat -n longfile.txt |глава -15|опашка -9 | grep " и\|и "


7 и всеки ред

11 и ако 13 командвате

Предходното обаче позволява команда обратно в микса. Следователно, ако наистина


искате да съпоставите конкретна дума, най-добре е да използвате тага -w , който е
достатъчно умен, за да се справи с вариантите:

cat -n longfile.txt |глава -15|опашка -9 | grep -w "и" 7 и всеки ред

11 и ако вие

Използването на интервал е по-безопасно, ако търсите нещо в началото или в края


на реда. Това е често срещан подход при четене на съдържанието на лог файлове или
друг структуриран текст, където първата дума често е важна (a
Machine Translated by Google

етикет като ГРЕШКА или Предупреждение, цифров код или дата). Тази команда показва
редовете, които започват с думата и:

cat longfile.txt |глава -15|опашка -9 | grep "^и"

Резултатът е тук (без номера на реда, защото не използваме cat -n):

и всеки ред и ако


вие

Спомнете си, че стилът „използване на имената на файловете в командата, вместо


използване на cat за показване на файла първо“ е по-ефективен:

глава -15 дълъг файл.txt |опашка -9 | grep "^and " и всеки ред

и ако ти

Командата head обаче не показва номерата на редовете на текстов файл, така че стилът
„cat first“ (cat -n добавя номера на редове) се използва в по-ранните примери, когато искате да
видите номерата на редовете, въпреки че този стил е по-малко ефективен. По принцип искате
да добавите допълнителна команда към канала само ако добавя стойност, в противен случай е
по-добре да започнете с директно извикване на файловете, които се опитвате да обработите с
първата команда в канала, като приемете синтаксиса на командата може да чете имена на
файлове.

ИЗПОЛЗВАНЕ НА ОБРАТНИ ПРЕПОРЪЧКИ В КОМАНДАТА GREP

The grep команда ви позволява да посочите набор от знаци, които съответстват на


регулярен израз, поставен в двойка скоби. За да може grep правилно да анализира скобите,
всяка трябва да бъде предшествана от символа за избягване „\“.

Например grep 'a\(.\)' използва "." регулярен израз за съвпадение на ab или a3 , но не и на


3a или ba.

Обратната препратка '\n', където n е една цифра, съвпада с подниза, съпоставен преди
това с n-тия подизраз в скоби на регулярния израз. Например grep '\(a\)\1' съвпада с aa и grep '\
(a\)\2' съвпада с aaa.
Machine Translated by Google

Когато се използва с редуване, ако групата не участва в мача, тогава


обратната препратка прави целия мач неуспешен. Например grep 'a\ (.\)|b\1'
няма да съответства на ba или ab или bb (или нещо друго).
Ако имате повече от един регулярен израз в двойка скоби,
те са посочени (отляво надясно) с \1, \2, …, \9:

• grep -e '\([az]\)\([0-9]\)\1' е същата като тази команда: grep -e '\([az]\)\([0-9]\) \


• ([az]\)' grep -e '\([az]\)\([0-9]\)\2' е същата като тази
• команда:

grep -e '\([az]\)\([0-9]\)\([0-9]\)'

Най-лесният начин да мислите за това е, че числото (например \2) е контейнер или


променлива, която ви спестява от въвеждането на по-дългия регулярен израз, към
който препраща. Тъй като регулярните изрази могат да станат изключително сложни,
това често помага за яснотата на кода.
Можете да съпоставите последователни цифри или знаци, като
използвате модела \([0-9]\)\1. Например следната команда е успешно
съвпадение, защото низът 1223 съдържа двойка последователни еднакви цифри:

ехо "1223" | grep -e '\([0-9]\)\1'

По същия начин, следната команда е успешно съвпадение, защото


низ 12223 съдържа три последователни срещания на цифрата 2:

ехо "12223" | grep -e '\([0-9]\)\1\1'

Можете да проверите за появата на две еднакви цифри, разделени с


произволен знак с този израз:

ехо "12z23" | grep -e '\([0-9]\).\1'

По аналогичен начин можете да тествате за появата на дублирани букви,


както е показано тук:

ехо "abbc" | grep -e '\([az]\)\1'

Следващият пример съвпада с IP адрес и не използва обратни препратки,


а само „\d“ и „\“. регулярни изрази за съвпадение на цифри и точки:
Machine Translated by Google

ехо "192.168.125.103" | grep -e '\(\d\d\d\)\.\(\d\d\d\)\.\(\d\d\d\)


\.\(\d\d\d\) '

Ако искате да разрешите по-малко от три цифри, можете да използвате израза {1,3},
който съвпада с 1, 2 или 3 цифри в третия блок. В ситуация, в която някой от четирите
блока може да има по-малко от три знака, трябва да използвате следния тип синтаксис
във всичките четири блока:

ехо "192.168.5.103" | grep -e '\(\d\d\d\)\.\(\d\d\d\)\.\(\d\)


\{1,3\}\.\(\d\d\ д\)'

Можете да извършите по-сложни съвпадения, като използвате обратни препратки.


Листинг 5.3 показва съдържанието на columns5.txt , който съдържа няколко реда, които
са палиндроми (същият правопис отляво надясно и отдясно наляво). Имайте предвид, че
третият ред е празен ред.

ОБЯВКА 5.3: columns5.txt

едно ено
ЕДНО ЕНО

ЕДНО ДВЕ OWT ENO


четири пет

Следната команда намира всички редове, които са палиндроми:

grep -w -e '\(.\)\(.\).*\2\1' columns5.txt

Резултатът от предходната команда е тук:

едно ено
ЕДНО ЕНО
ЕДНО ДВЕ OWT ENO

Идеята е следната: първият \(.\) съответства на набор от букви, последван от втори \(.\) , който съответства на набор

от букви, последван от произволен брой междинни знаци. Последователността \2\1 обръща реда на съвпадащите набори

от букви, определени от двете последователни срещания на \

(.\).

НАМИРАНЕ НА ПРАЗНИ РЕДОВЕ В НАБОРА ДАННИ


Machine Translated by Google

Спомнете си, че метасимволът ^ се отнася за началото на ред, а метасимволът $ се отнася


за края на реда. Така празен ред се състои от последователността ^$. Можете да намерите
единствения празен в columns5.txt с тази команда:

grep -n "^$" columns5.txt

Резултатът от предходната команда grep е тук (използвайте превключвателя -n , за да


покажете номерата на редовете, тъй като празните редове иначе няма да се показват в изхода):

3:

По-често целта е просто да премахнете празните редове от файл.


Можем да направим това просто като обърнем предишната заявка (и не показваме номерата
на редовете):

grep -v "^$" columns5.txt

едно ено
ЕДНО ЕНО
ЕДНО ДВЕ OWT ENO
четири пет

Както можете да видите, предходният изход показва четири непразни реда и


както видяхме в предишната команда grep , ред #3 е празен ред.

ИЗПОЛЗВАНЕ НА КЛЮЧОВЕ ЗА ТЪРСЕНЕ НА НАБОРИ ДАННИ

Данните често се организират около уникални стойности (обикновено числа) , за да се


разграничат иначе подобни неща: например мениджърът Джон Смит не трябва да се бърка с
програмиста Джон Смит в набор от данни за служител . Следователно на всеки запис се
присвоява уникален номер, който ще се използва за всички заявки, свързани със служители.
Освен това имената им са просто елементи от данни на даден запис, а не средство за
идентифициране на запис, който съдържа определено лице.

Имайки предвид предходните точки, да предположим, че имате текстов файл, в който


всеки ред съдържа една ключова стойност. Освен това друг текстов файл се състои от един или
повече реда, като всеки ред съдържа ключова стойност, последвана от стойност на количеството.
Machine Translated by Google

Като илюстрация листинг 5.4 показва съдържанието на skuvalues.txt , а листинг 5.5


показва съдържанието на skusold.txt. Обърнете внимание, че SKU е термин, който често
се използва за обозначаване на индивидуална продуктова конфигурация, включително
неговата опаковка, етикетиране и т.н.

ОБЯВКА 5.4: skuvalues.txt

4520
5530
6550
7200
8000

ОБЯВКА 5.5: skusold.txt

4520 12
4520 15
5530 5
5530 12
6550 0
6550 8
7200 50
7200 10
7200 30
8000 25
8000 45
8000 90

ЗНАКЪТ ОБРАТНА НАГЛЕЗАНА РИСКА И КОМАНДАТА GREP

Символът „\“ има специална интерпретация, когато е последван от следните знаци:

• '\b' = Съвпада с празния низ в края на дума • '\B' = Съвпада


с празния низ, при условие че не е в края на дума • '\brat\b' съвпада с отделната
дума 'rat', но не 'crate' • '\Brat\B' съвпада с 'crate', но не и с 'furry
rat' • '\<' = съвпада с празния низ в началото на
думата • '\>' = съвпада с празния низ в края на думата • '\w' =
Съвпадение на съставната дума; същото като '[_[:alnum:]]'
• '\W' = Съвпадение на съставна част, която не е дума; същото като
'[^_[:alnum:]]' • '\s' = Съответствие с интервал; същото като '[[:space:]]'
Machine Translated by Google

• '\S' = Съвпадение без празно пространство; същото като '[^[:space:]]'

МНОЖЕСТВО МАЧОВЕ В КОМАНДАТА GREP

В по-ранен пример видяхте как да използвате опцията -i за извършване на съпоставяне без


значение на малки и големи букви. Можете обаче да използвате и тръбата „|“ символ за указване
на повече от една последователност от регулярни изрази.
Например, следният grep израз съответства на всеки ред, който съдържа едно, както
и на всеки ред, който съдържа ЕДНО ДВЕ:

grep "едно\|ЕДНО ДВЕ" колони5.txt

Резултатът от предходната команда grep е тук:

едно ено
ЕДНО ДВЕ OWT ENO

Въпреки че предходната команда grep указва двойка символни низове, можете да


посочите произволен брой символни последователности или регулярни изрази, стига
да поставите "\|" между всяко нещо, което искате да съпоставите.

КОМАНДАТА GREP И КОМАНДАТА XARGS

The xargs командата често се използва заедно с командата find в bash. Например,
можете да търсите файловете в текущата директория (включително поддиректории),
които имат суфикс sh и след това да проверите кой от тези файлове съдържа низа abc,
както е показано тук:

намирам . -print |grep "sh$" | xargs grep -l abc

По-полезна комбинация от находката и тук: xargs е показана команда

намирам . -mtime -7 -име "*.sh" -print | xargs grep -l abc

Предходната команда търси всички файлове (включително поддиректории) със


суфикс „sh“ , които не са били модифицирани в поне седем
Machine Translated by Google

дни и предава този списък към командата xargs , която показва файловете, които
съдържат низа abc (без значение за малки и големи букви).

Командата find поддържа много опции, които могат да се комбинират чрез


И, както и ИЛИ за създаване на много сложни изрази.
Обърнете внимание, че grep -R hello . също така извършва търсене на низа hello in
всички файлове, включително поддиректории, и следва „един процес“
препоръка. Находката обаче . -print командата търси всички
файлове във всички поддиректории и можете да прехвърлите изхода към xargs grep hello
за да откриете срещанията на думата hello във всички файлове (което включва две
процеси вместо един процес).
Можете да използвате резултата от предходния кодов фрагмент, за да копирате съвпадението
файлове в друга директория, както е показано тук:

cp `намери. -print |grep "sh$" | xargs grep -l abc` /tmp

Като алтернатива можете да копирате съвпадащите файлове в текущата директория


(без съвпадащи файлове в поддиректории) в друга директория с
grep команда:

cp `grep -l abc *sh` /tmp

Още един подход е да използвате обратна отметка, за да можете да получите допълнителни


информация:

за файл в `намери . -печат`


направи

echo "Обработване на файла: $file"


# сега направете нещо тук
Свършен

Ако подадете твърде много имена на файлове към командата xargs , ще видите „също
много файлове“ съобщение за грешка. В тази ситуация опитайте да вмъкнете grep
допълнителни команди преди
xargsкомандата, за да намалите броя на файловете, които са
въведени в командата.
xargs
Ако работите с NodeJS, знаете, че директорията node_modules
съдържа голям брой файлове. В повечето случаи вероятно искате да изключите
файловете в тази директория, когато търсите низ, и -v
опцията е идеална за тази ситуация. Следващата команда изключва файловете
в директорията node_modules , докато търсите имената на HTML
Machine Translated by Google

файлове, които съдържат низа src и пренасочване на списъка с имена на файлове към
файла src_list.txt (и също пренасочване на съобщения за грешка към /dev/null):

намирам . -print |grep -v възел |xargs grep -il src >src_list.txt 2>/dev/null

Можете да разширите предходната команда, за да търсите HTML файловете, които


съдържат низа src и низа angular със следната команда:

намирам . -print |grep -v възел |xargs grep -il src |xargs grep -il angular >angular_list.txt 2>/dev/null

Можете да използвате следната комбинация, grep и xargs за намиране на файловете


която съдържа xml и defs:

grep -l xml *svg |xargs grep -l def

Вариант на предходната команда пренасочва съобщенията за грешка към


/dev/null, както е показано тук:

grep -l здравей *txt 2>/dev/null | xargs grep -c здравей

Търсене на низ в zip файлове

Има поне три начина за търсене на низ в един или повече zip файлове. Като пример,
да предположим, че искате да определите кои zip файлове съдържат SVG документи.

Първият начин е показан тук:

за f в `ls *zip` направете

echo "Търсене на $f" jar tvf $f |grep


"svg$" готово

Когато има много zip файлове в директория, изходът от предходния цикъл може да
бъде подробен, в който случай трябва да превъртите назад и вероятно да копирате/
поставите имената на файловете, които всъщност съдържат SVG документи, в отделен
файл. По-добро решение е да поставите предходния цикъл в обвивка и да пренасочите
изхода му. Например, създайте файла findsvg.sh , чието съдържание е предходният цикъл,
и след това извикайте тази команда:
Machine Translated by Google

./findsvg.sh 1>1 2>2

Забележете, че предходната команда пренасочва съобщението за грешка (2>) към


файл 2 и резултатите от командата jar/grep (1>) към файл 1.

ПРОВЕРКА ЗА УНИКАЛНА КЛЮЧОВА СТОЙНОСТ

Понякога трябва да проверите за съществуването на низ (като ключ) в текстов файл


и след това да извършите допълнителна обработка въз основа на неговото съществуване.
Не приемайте обаче, че съществуването на низ означава, че този низ се среща само
веднъж. Като прост пример, да предположим, че файлът mykeys.txt има следното
съдържание:

2000
22000
10000
3000

Да предположим, че търсите низа 2000, което можете да направите с findkey.sh, чието


съдържание е показано в листинг 5.6.

ОБЯВКА 5.6: findkey.sh

ключ="2000"

if [ "`grep $key mykeys.txt`" != "" ] тогава

foundkey=истина друго

foundkey=false fi

echo "текущ ключ = $key" echo "намерен


ключ = $foundkey"

Списък 5.6 съдържа условна логика if/else за определяне дали файлът mykeys.txt
съдържа стойността на $key (която е инициализирана като 2000).
Стартирайте кода в листинг 5.6 и ще видите следния резултат:

текущ ключ = 2000 намерени


ключ = истински брой линии = 2
Machine Translated by Google

Въпреки че стойността на ключа 2000 съществува в mykeys.txt, можете да видите, че тя


съвпада с два реда в mykeys.txt. Въпреки това, ако mykeys.txt беше част от файл
със 100 000 (или повече) реда не е очевидно, че стойността от 2000 съвпада
повече от един ред. В този набор от данни 2000 и 22000 съвпадат и можете
предотврати допълнителния съвпадащ ред с този кодов фрагмент:

grep -w $ ключ

По този начин във файлове, които имат дублирани редове, можете да преброите броя на редовете
които съответстват на ключа чрез предходния кодов фрагмент. Друг начин да го направите
включва използването на wc -l, което показва броя на редовете.

Пренасочване на съобщения за грешка

Друг сценарий включва използването на командата, xargs команда с grep


което може да доведе до съобщения за грешка „няма такова...“:

намирам . -print |xargs grep -il abc

Уверете се, че пренасочвате грешките, като използвате следния вариант:

намирам . -print |xargs grep -il abc 2>/dev/null

КОМАНДАТА egrep И КОМАНДАТА fgrep

The егреп командата е удължена grep който поддържа grep Характеристика

добавяне като „+“ (1 или повече срещания на предишен знак), „?“ (0 или 1
появяване на предишен знак) и „|“ (алтернативно съвпадение). Командата е почти егреп
идентична с grep -E, заедно с някои предупреждения, които са
описан онлайн:

https://www.gnu.org/software/grep/manual/html_node/Basic-vs-Extended.html

Едно предимство на използването на командата


егреп е, че е по-лесно
разбират регулярните изрази, отколкото съответните изрази в
grep (когато се комбинира с обратни препратки).
Machine Translated by Google

The Командата egrep („разширен grep“) поддържа разширено редовно


изрази, както и тръбата „|“ за да посочите няколко думи в търсене
модел. Съвпадението е успешно, ако някоя от думите в шаблона за търсене
се появява, така че можете да мислите за модела за търсене като за „всяко“ съвпадение. По този начин,
шаблонът abc|def съответства на редове, които съдържат или abc , или def (или и двете).
Например следният кодов фрагмент ви позволява да търсите
срещания на низа abc , както и срещания на низа def във всички файлове
с наставката sh:

egrep -w 'abc|def' *sh

Предходната команда egrep е операция "или": ред съвпада, ако тя


съдържа или abc , или def.
Можете също да използвате метасимволи в egrep изрази. Например, на

следният кодов фрагмент съвпада с редове, които започват с abc или завършват с четири и
празно място:

egrep '^123|четири $' колони3.txt

По-подробно обяснение на онлайн: grep, egrep, и fgrep е наличен

https://superuser.com/questions/508881/what-is-the-dif erence-between-grep-
pgrep-egrep-fgrep

Показване на „чисти“ думи в набор от данни с egrep

За простота, нека работим с текстов низ и по този начин можем да видим


междинни резултати, докато работим към решението. Нека инициализираме
променлива x , както е показано тук:

x="ghi abc Ghi 123 #def5 123z"

Първата стъпка е да разделите x на думи:

'
ехо $x |tr -s ' '\н'

Резултатът е тук:
Machine Translated by Google

ghi
абв
Ghi
123
#def5
123z

Втората стъпка е извикване егреп с регулярния израз ^[a-zA-


Z]+, който съответства на всеки низ, състоящ се от един или повече главни букви и
малки букви (и нищо друго):

'
ехо $x |tr -s ' '\n' |egrep "^[a-zA-Z]+$"

Резултатът е тук:

ghi
абв
Ghi

Ако също искате да сортирате изхода и да отпечатате само уникалните думи, използвайте
тази команда:

'
ехо $x |tr -s ' '\n' |egrep "^[a-zA-Z]+$" |sort | уникален

Резултатът е тук:

123
123z
Ghi
абв
ghi

Ако искате да извлечете само целите числа в променливата x, използвайте това


команда:

'
ехо $x |tr -s ' '\n' |egrep "^[0-9]+$" |sort | уникален

Резултатът е тук:

123

Ако искате да извлечете буквено-цифрови думи от променливата x, използвайте това


команда:
Machine Translated by Google

'
ехо $x |tr -s ' '\n' |egrep "^[a-zA-Z0-9]+$" |sort | уникален

Резултатът е тук:

123
123z
Ghi
абв
ghi

Обърнете внимание, че последователностите за съпоставяне на ASCII поставят цифри преди


главните букви, а последните са преди малките букви поради следната причина: 0 до 9 са
шестнадесетични стойности 0x30 до 0x39, главните букви в AZ са шестнадесетични 0x41 до 0x5a, а
малките букви в шестнадесетичен 0x61 до 0x7a. az са

Можете да замените echo $x с набор от данни, за да извлечете само азбучни низове от този
набор от данни.

Командата fgrep fgrep

(„бърза грешка“) е същата като grep -F и въпреки че fgrep е отхвърлена, тя все още се
поддържа, за да позволи на историческите приложения, които разчитат на тях, да работят
немодифицирани. В допълнение, някои по-стари системи може да не поддържат опцията -F за
командата grep , така че те използват командата fgrep . Ако наистина искате да научите повече за
командата fgrep , потърсете уроци в Интернет.

ПРОСТ СЛУЧАЙ НА УПОТРЕБА

Примерният код в този раздел ви показва как да използвате за да команда grep ,

намерите конкретни редове в набор от данни и след това да „слеете“ двойки редове, за да създадете нов набор от данни. Това е

много подобно на това, което прави командата за присъединяване в релационна база данни. Листинг 5.7 показва съдържанието

на файла test1.csv , който съдържа първоначалния набор от данни.

ОБЯВКА 5.7: test1.csv

F1,F2,F3,M0,M1,M2,M3,M4,M5,M6,M7,M8,M9,M10,M11,M12
1,KLM,,1.4,,0.8,,1.2,,1.1,,,2.2 ,,,1.4
1,KLMAB,,0.05,,0.04,,0.05,,0.04,,,0.07,,,0.05
Machine Translated by Google

1,TP,,7.4,,7.7,,7.6,,7.6,,,8.0,,,7.3
1,XYZ,,4.03,3.96,,3.99,,3.84,4.12,,,,4.04,, 2,KLM, ,0.9,0.7,,0.6,,0.8,0.5,,,,0.5,,
2,KLMAB,,0.04,0.04,,0.03,,0.04,0.03,,,,0.03,,
2,EGFR,,99,99 ,,99,,99,99,,,,99,, 2,TP,,6.6,6.7,,6.9,,6.6,7.1,,,,7.0,,
3,KLM,,0.9,0.1,,0.5, ,0.7,,0.7,,,0.9,,
3,KLMAB,,0.04,0.01,,0.02,,0.03,,0.03,,,0.03,,
3,PLT,,224,248,,228,,251,,273, ,,206,,
3,XYZ,,4.36,4.28,,4.58,,4.39,,4.85,,,4.47,, 3,RDW,,13.6,13.7,,13.8,,14.1,,14.0,,,13.4, ,
3,WBC,,3.9,6.5,,5.0,,4.7,,3.7,,,3.9,, 3,A1C,,5.5,5.6,,5.7,,5.6,,5.5,,,5.3,,
4,KLM ,,1.2,,0.6,,0.8,0.7,,,0.9,,,1.0, 4,TP,,7.6,,7.8,,7.6,7.3,,,7.7,,,7.7,
5,KLM,,0.7, ,0.8,,1.0,0.8,,0.5,,,1.1,, 5,KLM,,0.03,,0.03,,0.04,0.04,,0.02,,,0.04,,
5,TP,,7.0,,7.4,, 7.3,7.6,,7.3,,,7.5,,
5,XYZ,,4.73,,4.48,,4.49,4.40,,,4.59,,,4.63,

Листинг 5.8 показва съдържанието на файла joinlines.sh , който илюстрира


как да обедините двойките съвпадащи редове в joinlines.csv.

ОБЯВКА 5.8: joinlines.sh

inputfile="test1.csv"
outputfile="joinedlines.csv" tmpfile2="tmpfile2"

# шаблони за съвпадение:
klm1="1,KLM,"
klm5="5,KLM,"
xyz1="1,XYZ,"
xyz5="5,XYZ,"

#изход:
#klm1,xyz1
#klm5,xyz5

# стъпка 1: съвпадение на шаблони с CSV файл: klm1line="`grep


$klm1 $inputfile`" klm5line="`grep $klm5 $inputfile`"
xyz1line="`grep $xyz1 $inputfile`" # $xyz5 съвпада с 2
реда ( искаме първи ред): grep $xyz5 $inputfile >
$tmpfile2 xyz5line="`head -1 $tmpfile2`" echo "klm1line: $klm1line" echo
"klm5line: $klm5line" echo "xyz1line: $xyz1line"
Machine Translated by Google

echo "xyz5line: $xyz5line"

# стъпка 3: създайте обобщен файл:


ехо "$klm1line" | tr -d '\n' > $изходен файл
ехо "$xyz1line" >> $изходен файл
ехо "$klm5line" | tr -d '\n' >> $изходен файл
ехо "$xyz5line" >> $изходен файл
ехо; ехо

Резултатът от стартирането на shell скрипта в листинг 5.14 е тук:

1,KLM,,1.4,,0.8,,1.2,,1.1,,,2.2,,,1.41,X
YZ,,4.03,3.96,,3.99,,3.84,4.12,,,,4.04,,
5,KLM,,0.7,,0.8,,1.0,0.8,,0.5,,,1.1,,5,KLM
,,0.03,,0.03,,0.04,0.04,,0.02,,,0.04,,5,X
YZ,,4.73,,4.48,,4.49,4.40,,,4.59,,,4.63,

Както можете да видите, задачата в този раздел се решава лесно чрез grep
команда. Обърнете внимание, че е необходимо допълнително почистване на данни, за да се справи с празните

полета в изхода.

РЕЗЮМЕ

Тази глава ви показа как да работите с помощната програма grep , която е a


мощна Unix команда за търсене на текстови полета за низове. Видяхте
различни опции за командата
grep и примери как да ги използвате
опции за намиране на модели на низове в текстови файлове.

След това научихте за egrep, вариант на командатаgrep , която може


опростете и разширете основната функционалност на grep, като посочите кога вие
може да избере една опция пред друга.
Най-накрая научихте как да използвате ключови стойности в един текстов файл, за да търсите
съвпадение на редове от текст в друг файл и извършване на подобни на присъединяване операции с помощта
на grep команда.
Machine Translated by Google

ГЛАВА 6

ТРАНСФОРМИРАНЕ НА ДАННИ СЪС sed

В предишната глава научихме как да намалим поток от данни само до


аз
съдържание, което ни интересува. В тази глава научаваме как да трансформираме тези данни
с помощта на помощната програма Unix sed , която е акроним за „редактор на потоци“.
Първата част на тази глава съдържа основни примери за командата sed , като замяна и
изтриване на низове, цифри и букви. Втората част на тази глава обсъжда различни
превключватели, които са налични за командата sed , заедно с пример за замяна на
множество разделители с един разделител в набор от данни.

В последния раздел ще видите редица примери за това как да извършвате потоково


ориентирана обработка на набори от данни, обединявайки възможностите на sed заедно с
командите и регулярните изрази от предишни глави, за да изпълните трудни задачи със
сравнително прост код.

КАКВО Е КОМАНДАТА sed?

Името sed е акроним за „редактор на потоци“ и помощната програма извлича много от


своите команди от редовия редактор ed (ed беше първият текстов редактор на Unix).
Командата sed е „неинтерактивен“ ориентиран към поток редактор, който може да се
използва за автоматизиране на редактирането чрез скриптове на обвивката. Тази възможност
за модифициране на цял поток от данни (които могат да бъдат съдържанието на множество
файлове, по начин, подобен на начина, по който grep се държи), сякаш сте в редактор, не е

често срещана в съвременните езици за програмиране. Това поведение позволява някои


възможности, които не могат лесно да се дублират другаде, като същевременно се държи точно като всяка др
Machine Translated by Google

команда (grep, cat, ls, find и т.н.) в начина, по който може да приема данни, изходни данни и
съвпадение на шаблони с регулярни изрази.
Някои от по-честите употреби на sed включват отпечатване на съвпадащи редове, изтриване
на съвпадащи редове и намиране/замяна на съвпадащи низове или регулярни изрази.

Цикълът на изпълнение на sed

Всеки път, когато извикате командата sed , цикълът на изпълнение се отнася до различни
опции, които са посочени и се изпълняват, докато се достигне краят на файла/входа. По-конкретно,
един цикъл на изпълнение изпълнява следните стъпки:

• Чете цял ред от stdin/файл. • Премахва всеки

следващ нов ред. • Поставя линията в своя


буфер за шаблони. • Модифицира шаблонния
буфер според предоставените команди. • Отпечатва шаблонния буфер на stdout.

СЪВМЕСТВАНЕ НА ШАБЛОНИ НА НИЗОВЕ С ИЗПОЛЗВАНЕ НА SED

Командата sed изисква да посочите низ, който да съответства на редовете във файл. Да
предположим например, че файлът numbers.txt съдържа следните редове:

12
123

3 пет
4

Следната команда sed отпечатва всички редове, които съдържат низа 3:

cat numbers.txt |sed -n "/3/p"

Друг начин за получаване на същия резултат е следният:

sed -n "/3/p" numbers.txt

И в двата случая резултатът от предходните команди е както следва:


Machine Translated by Google

123
3

Както видяхме по-рано с други команди, винаги е по-ефективно просто да прочетете


файла с помощта на командата sed , отколкото да го въведете с друга команда. Можете да го
„захраните“ с данни от друга команда, при условие че другата команда добавя стойност (като
добавяне на номера на редове, премахване на празни редове или други подобни полезни
дейности).
Опцията -n потиска целия изход, а опцията p отпечатвасъвпадащия ред. Ако пропуснете
опцията -n , тогава всеки ред се отпечатва, а опцията p кара съответстващия ред да бъде
отпечатан отново. Следователно можете да подадете следната команда:

sed "/3/p" numbers.txt

Резултатът (данните вдясно от двоеточието) е както следва. Обърнете внимание, че


етикетите отляво на двоеточието показват източника на данните, за да илюстрират
поведението „ред по ред“ на sed.

Основен изходен поток: 1


Основен изходен поток: 2
Основен изходен поток: 123
Шаблон Съвпадащ текст:123
Основен поток изход:3
Шаблон Съвпадащ текст:3
Основен изходен поток: пет
Основен изходен поток :4

Също така е възможно да съпоставите два шаблона и да отпечатате всичко между


редовете, които съвпадат:

sed -n "/123/,/пет/p" числа.txt

Резултатът от предходната команда (всички редове между 123 и пет, включително) е тук:

123
3
Пет

ЗАМЕСТВАНЕ НА ШАБЛОНИ НА НИЗОВЕ С ИЗПОЛЗВАНЕ НА SED


Machine Translated by Google

Примерите в този раздел илюстрират как да използвате sed за заместване на нов


текст за съществуващ текстов модел.

x="abc" ехо
$x |sed "s/abc/def/"

Резултатът от предходния кодов фрагмент е тук:

деф

В предишната команда сте инструктирали sed да замени ("s) първия текст


шаблон (/abc) с втория шаблон (/def) и без допълнителни инструкции (/").
Изтриването на текстов шаблон е просто въпрос на оставяне на втория шаблон
празен:

ехо "abcdefabc" |sed "s/abc//"

Резултатът е тук:

defabc

Както виждате, това премахва само първото появяване на шаблона. Можете да


премахнете всички срещания на шаблона, като добавите „глобалната“ терминална
инструкция (/g"):

ехо "abcdefabc" |sed "s/abc//g"

Резултатът от предходната команда е тук:

деф

Обърнете внимание, че работим директно с основния поток с тази команда, тъй


като не използваме тага -n . Можете също така да потиснете основния поток с -n и да
отпечатате заместването, постигайки същия резултат, ако използвате инструкцията p
(отпечатване) на терминала:

ехо "abcdefabc" |sed -n "s/abc//gp"

деф
Machine Translated by Google

За замествания и двата синтаксиса са подходящи, но това не винаги е вярно за


други команди.
Можете също да премахнете цифри вместо букви, като използвате цифровия мета-
символи като модел за съвпадение на вашия регулярен израз (от глава 1):

ls svcc1234.txt |sed "s/[0-9]//g" ls svcc1234.txt |sed -n "s/[0-9]//


gp"

Резултатът от една от двете предходни команди е тук:

svcc.txt

Спомнете си, че файлът columns4.txt съдържа следния текст:

123 ЕДНО ДВЕ


456 три четири
ЕДНО ДВЕ ТРИ ЧЕТИРИ пет 123
шест едно две три

четири пет

Следната команда sed е инструктирана да идентифицира редовете между 1


и 3, включително ("1,3) и ги изтрийте (d") от изхода:

котка колони4.txt | sed "1,3d"

Резултатът е тук:

пет 123 шест


едно две три четири
пет

Следващата команда sed изтрива набор от редове, започвайки от реда, който


съответства на 123 , и продължава през файла, докато стигне до реда, който съответства
на низа пет (и също изтрива всички междинни редове). Синтаксисът трябва да е познат
от предишния пример за съвпадение:

sed "/123/,/пет/d" колони4.txt

Резултатът е тук:

едно две три


Machine Translated by Google

четири пет

Замяна на гласни от низ или файл

Следният кодов фрагмент ви показва колко лесно е да замените няколко гласни


от низ с помощта на командата sed :

ехо "здравей" | sed "s/[aeio]/u/g"

Резултатът от предходния кодов фрагмент е тук:

Хулу

Изтриване на множество цифри и букви от низ

Да предположим, че имаме променлива x , която е дефинирана по следния начин:

x="a123zAB 10x b 20 c 300 d 40w00"

Спомнете си, че цялото число се състои от една или повече цифри, така че
съответства на регулярния израз [0-9]+, който съответства на една или повече цифри.
Трябва обаче да укажете регулярния израз [0-9]* , за да премахнете всяко число от
променливата x:

ехо $x | sed "s/[0-9]//g"

Резултатът от предходната команда е тук:

azAB xbcdw

Следната команда премахва всички малки букви от променливата


х:

ехо $x | sed "s/[az]*//g"

Резултатът от предходната команда е тук:

123AB 10 20 300 4000

Следната команда премахва всички малки и главни букви от променливата x:


Machine Translated by Google

ехо $x | sed "s/[az][AZ]*//g"

Резултатът от предходната команда е тук:

123 10 20 300 4000

ТЪРСЕНЕ И ЗАМЕНЯНЕ СЪС SED

Предишният раздел ви показа как да изтриете диапазон от редове на текстов


файл въз основа на начален и краен ред, като използвате или числов диапазон,
или чифт низове. Тъй като изтриването е просто заместване на празен резултат
за това, което съвпадате, сега трябва да е ясно, че дейността по замяна включва
попълване на тази част от командата с нещо, което постига желания от вас
резултат. Този раздел съдържа различни примери, които илюстрират как да
получите точната замяна, която желаете.
Следните примери илюстрират как да конвертирате малки букви abc в главни букви ABC в sed:

ехо "abc" |sed "s/abc/ABC/"

Резултатът от предходната команда е тук (която работи само в един случай на abc):

ABC

ехо "abcdefabc" |sed "s/abc/ABC/g"

Резултатът от предходната команда е тук (/g” означава, че работи във всеки случай на abc):

ABCdefABC

Следният sed израз извършва три последователни замествания, като използва


-e, за да ги съедини в низове. Променя точно едно (първото) a на A, едно b на B,
едно c на C:

ехо "abcde" |sed -e "s/a/A/" -e "s/b/B/" -e "s/c/C/"

Резултатът от предходната команда е тук:


Machine Translated by Google

АбВГД

Очевидно можете да използвате следния sed израз, който комбинира трите


замествания в едно заместване:

ехо "abcde" |sed "s/abc/ABC/"

Независимо от това ключът -e е полезен, когато трябва да изпълнявате повече


сложни замени, които не могат да бъдат комбинирани в една замяна.
Знакът “/” не е единственият разделител, който sed поддържа, което е полезно,
когато низовете съдържат знака “/” . Например, можете да обърнете реда на /aa/bb/cc/
с тази команда:

ехо "/aa/bb/cc" |sed -n "s#/aa/bb/cc#/cc/bb/aa/#p"

Резултатът от предходната команда sed е тук:

/cc/bb/aa/

Следните примери илюстрират как да използвате командата w terminal за запис


на изхода на sed както в стандартния изход, така и в именуван файл upper1 , ако
съвпадението е успешно:

ехо "abcdefabc" |sed "s/abc/ABC/wupper1"


ABCdefabc

Ако разгледате съдържанието на текстовия файл upper1, ще видите, че той


съдържа същия низ ABCdefabc , който се показва на екрана. Това поведение на два
потока, което забелязахме по-рано с командата за печат („p“) на терминала, е
необичайно, но понякога полезно. По-обичайно е просто да изпратите стандартния
изход към файл, като използвате синтаксиса „>“, както е показано по-долу (и двата
синтаксиса работят за операция за замяна), но в този случай нищо не се записва на
екрана на терминала. Предишният синтаксис позволява и двете едновременно:

ехо "abcdefabc" | sed "s/abc/ABC/" > upper1 ехо "abcdefabc" | sed


-n "s/abc/ABC/p" > горен1

Листинг 6.1 показва съдържанието на update2.sh , което замества появата на низа


hello с низа goodbye във файловете с
Machine Translated by Google

суфикс txt в текущата директория.

ОБЯВКА 6.1: update2.sh

за f в `ls *txt`
do

newfile="${f}_new" cat $f | sed -n "s/


hello/goodbye/gp" > $newfile mv $newfile $f done

Списък 6.1 съдържа for цикъл, който обхожда списъка с текстови файлове със
суфикс txt . За всеки такъв файл инициализирайте променливата newfile , която се
създава чрез добавяне на низа _new към първия файл (представен от променливата
f). След това заменете срещанията на hello с низа goodbye във всеки файл f и
пренасочете изхода към $newfile. Накрая преименувайте $newfile на $f с помощта на
командата mv .
Ако искате да извършите актуализацията в съвпадащи файлове във всички
поддиректории, заменете командата for със следното:

за f в „намери“. -print |grep "txt$"`

НАБОРИ ДАННИ С МНОЖЕСТВО РАЗДЕЛИТЕЛИ

Листинг 6.2 показва съдържанието на набора от данни delim1.txt, който съдържа


множество разделители |, : и ^. Листинг 6.3 показва съдържанието на delimiter1.sh,
което илюстрира как да замените различните разделители в delimiter1.txt с един
разделител със запетая (,).

ОБЯВКА 6.2: delimiter1.txt

1000|Джейн:Едуардс^Продажби
2000|Том:Смит^Разработка
3000|Дейв:Дел Рей^Маркетинг

ЛИСТИНГ 6.3: разделител1.sh

inputfile="delimiter1.txt" cat $inputfile |


sed -e 's/:/,/' -e 's/|/,/' -e 's/\^/,/'
Machine Translated by Google

Както можете да видите, вторият ред в листинг 6.3 е прост, но мощен: можете да разширите
командата sed с толкова разделители, колкото искате, за да създадете набор от данни с един
разделител между стойностите. Резултатът от листинг 6.3 е показан тук:

1000, Джейн, Едуардс, Продажби


2000, Том, Смит, развитие
3000, Дейв, Дел Рей, Маркетинг

Този вид трансформация може да бъде опасна, освен ако не сте проверили дали вашият
нов разделител вече не се използва. За това е полезна команда grep (искате резултатът да е
нула):

grep -c ',' $inputfile 0

ПОЛЕЗНИ ПРЕВКЛЮЧВАТЕЛИ В SED

Трите превключвателя на командния ред -n, -e, и -i са полезни, когато вие


задайте ги с командата sed .
Като преглед, укажете -n , когато искате да потиснете отпечатването на основния изходен
поток:

sed -n 's/foo/bar/'

Посочете -n и завършете с /p' , когато искате да съпоставите само резултата:

sed -n 's/foo/bar/p'

Накратко се спряхме на използването на -e за извършване на множество замествания, но


може да се използва и за комбиниране на други команди. Този синтаксис ни позволява да
разделим командите в последния пример:

sed -n -e 's/foo/bar/' -e 'p'

По-усъвършенстван пример, който загатва за гъвкавостта на sed , включва вмъкването на


символ след фиксиран брой позиции. Например, разгледайте следния кодов фрагмент:

ехо "ABCDEFGHIJKLMNOPQRSTUVWXYZ" | sed "s/.\{3\}/&\n/g"


Machine Translated by Google

Резултатът от предходната команда е тук:

ABCnDEFnGHInJKLnMNOnPQRnSTUnVWXnYZ

Докато горният пример не изглежда особено полезен, помислете за голям текстов поток
без прекъсвания на редове (всичко на един ред). Можете да използвате нещо подобно, за да
вмъкнете знаци за нов ред, или нещо друго, за да разделите данните на по-лесни за обработка
части. Възможно е да работите с точно това, което прави sed , като разгледате всеки елемент
от командата и го сравните с изхода, дори ако не знаете синтаксиса. (Понякога ще срещнете
много сложни инструкции за sed без никаква документация в кода.)

Резултатът се променя след всеки три знака и знаем, че точката (.) съвпада с всеки
отделен знак, така че .{3} трябва да му казва да направи това (с екраниращи наклонени черти
(\)), защото скобите са специален знак за sed и няма да го интерпретира правилно, ако просто
го оставим като .{3}. n е достатъчно ясен в колоната за заместване, така че &\ трябва да му
казва да вмъкне знак, вместо да го замени. Командата g на терминала , разбира се, означава
„повтаряне“. За да изясните и потвърдите тези предположения, вземете това, което можете
да заключите, и извършете търсене в Интернет.

РАБОТА С НАБОРИ ДАННИ

Помощната програма sed е полезна за манипулиране на съдържанието на текстови


файлове. Например, можете да отпечатате диапазони от редове и подмножества от редове,
които съответстват на регулярен израз. Можете също да извършите търсене и замяна на
редовете в текстов файл. Този раздел съдържа примери, които илюстрират как да изпълнявате
такава функционалност.

Отпечатване на

редове Списък 6.4 показва съдържанието на test4.txt (редове с двойно разстояние), които
се използват за няколко примера в този раздел.

ОБЯВКА 6.4. test4.txt

абв

деф
Machine Translated by Google

абв

абв

Следният кодов фрагмент отпечатва първите 3 реда в test4.txt (ние използвахме


този синтаксис преди при изтриване на редове; то е еднакво полезно за печат):

cat test4.txt |sed -n "1,3p"

Резултатът от предходния кодов фрагмент е тук (вторият ред е празен):

абВ
ГДЕ

Следният кодов фрагмент отпечатва редове от 3 до 5 в test4.txt:

cat test4.txt |sed -n "3,5p"

Резултатът от предходния кодов фрагмент е тук:

деф

абв

Следният кодов фрагмент се възползва от основния изходен поток и втория поток


за съвпадение, за да дублира всеки ред (включително празни редове) в
test4.txt:

cat test4.txt |sed "p"

Резултатът от предходния кодов фрагмент е тук:

абв
абв

деф
деф

абв
абв
Machine Translated by Google

абв
абв

Следният кодов фрагмент отпечатва първите три реда и след това прави с главни букви
низа abc, дублирайки ABC в крайния изход, защото не използвахме -n и завършвахме с /p"
във втората команда sed . Не забравяйте, че /p" отпечатва само текст, който съответства на
командата sed , където основният изход отпечатва целия файл, поради което def не се
дублира:

cat test4.txt |sed -n "1,3p" |sed "s/abc/ABC/p"

ABC
ABC

деф

Класове на знаци и sed

Можете също да използвате регулярни изрази със sed. Като напомняне, ето съдържанието на columns4.txt:

123 ЕДНО ДВЕ 456


три четири
ЕДНО ДВЕ ТРИ ЧЕТИРИ
пет 123 шест
едно две три четири
пет

Като наш първи пример, включващ sed и символни класове, следното


кодовият фрагмент илюстрира как да съпоставите редове, които съдържат малки букви:

котка колони4.txt | sed -n '/[0-9]/p'

Резултатът от предходния фрагмент е тук:

едно две три


едно две
едно две три четири
един
едно три
едно четири

Следният кодов фрагмент илюстрира как да съпоставите редове, които съдържат малки
букви:
Machine Translated by Google

котка колони4.txt | sed -n '/[az]/p'

Резултатът от предходния фрагмент е тук:

123 ЕДНО ДВЕ 456


три четири пет 123 шест

Следният кодов фрагмент илюстрира как да съпоставите редове, които съдържат числата
4, 5 или 6:

котка колони4.txt | sed -n '/[4-6]/p'

Резултатът от предходния фрагмент е тук:

456 три четири

Следният кодов фрагмент илюстрира как да съпоставите редове, които започват с

всеки два знака, последвани от EE:

котка колони4.txt | sed -n '/^.\{2\}EE*/p'

Резултатът от предходния фрагмент е тук:

ЕДНО ДВЕ ТРИ ЧЕТИРИ

Премахване на контролни знаци

Листинг 6.5 показва съдържанието на controlchars.txt. Контролни знаци


от всякакъв вид може да бъде премахнат от sed точно както всеки друг символ.

ОБЯВКА 6.5. controlchars.txt

1 връщане на каретка^M 2 връщане


на каретка^M 1 табулатор^I

Следната команда премахва символите за връщане на каретката и табулатора от текстовия


файл ControlChars.txt:

cat controlChars.txt | sed "s/^M//" |sed "s/ //"


Machine Translated by Google

Не можете да видите табулатора във втората команда sed в предходния кодов


фрагмент; ако обаче пренасочите изхода към файла nocontrol1.txt, можете да видите,
че в този нов файл няма вградени контролни знаци, като напишете следната команда:

cat -t nocontrol1.txt

БРОЕНЕ НА ДУМИ В НАБОР ДАННИ

Листинг 6.6 показва съдържанието на WordCountInFile.sh, което илюстрира как да


комбинирате различни bash команди, за да преброите думите (и техните срещания)
във файл.

ОБЯВКА 6.6. wordcountinfile.sh

# Как работи този примерен код: # Файлът се подава


към командата "tr", # командата "tr" променя главните на малки букви
# sed премахва запетаите и точките, след което променя интервалите на нови редове #
uniq се нуждае от всяка дума на отделен ред за правилно преброяване на думите # uniq преобразува данните
в уникални

думи # uniq също показва броя на появяванията на всяка дума # Окончателното сортиране подрежда данните по броя на
думите.

котка "$1" | xargs -n1 | tr AZ az | \ sed -e 's/\.//g' -e 's/\,//g' -e 's/ /\ /g' |


\ сортиране | уникален -c | сортиране -nr

Предишната команда изпълнява следните операции:

• Избройте всяка дума във всеки ред на файла. •


Преместете символите към малки

букви. • Филтриране на точки и запетаи.


• Промяна на разстоянието между думите към
преместване на ред. • Премахване на дубликати, префикс брой срещания и сортиране числово.

ОБРАТНИ ПРЕПОРЪЧКИ В SED

В главата, описваща grep, научихте за обратните препратки и подобна


функционалност е достъпна с командата sed . Основната разлика
Machine Translated by Google

е, че обратните препратки могат да се използват и в секцията за заместване на


командата.
Следната команда sed съвпада с последователните букви „a“ и отпечатва четири
от тях:

ехо "aa" |sed -n "s#\([az]\)\1#\1\1\1\1#p"

Резултатът от предходния кодов фрагмент е тук:

аааа

Следната команда sed замества всички дублирани двойки букви с буквите aa:

ехо "aa/bb/cc" |sed -n "s#\(aa\)/\(bb\)/\(cc\)#\1/\1/\1/#p"

Резултатът от предишната команда sed е тук (обърнете внимание на знака „/“ в


края):

аа/аа/аа/

Следната команда вмъква запетая в четирицифрено число:

echo "1234" |sed -n "s@\([0-9]\)\([0-9]\)\([0-9]\)\([0-9]\)@\1 ,\2\3\4@p"

Предходната команда sed използва знака @ като разделител. Класът на знаците


[0-9] съвпада с една единствена цифра. Тъй като във входния низ 1234 има четири
цифри , класът на знаците [0-9] се повтаря 4 пъти и стойността на всяка цифра се
съхранява в \1, \2, \3 и \4. Резултатът от предходната команда sed е тук:

1,234

По-общ sed израз, който може да вмъкне запетая в петцифрени числа, е тук:

ехо "12345" | sed 's/\([0-9]\{3\}\)$/,\1/g;s/^,//'

Резултатът от предходната команда е тук:


Machine Translated by Google

12,345

ПОКАЗВАНЕ САМО НА „ЧИСТИ“ ДУМИ В НАБОР ДАННИ

В предишната глава решихме тази задача с помощта на командата egrep , а този


раздел ви показва как да решите тази задача с помощта на командата sed .
За простота, нека работим с текстов низ, за да можем да видим междинните
резултати, докато работим към решението. Подходът ще бъде подобен на кодовия
блок, показан по-рано, който брои уникални думи. Нека инициализираме променливата
x , както е показано тук:

x="ghi abc Ghi 123 #def5 123z"

Първата стъпка е да разделите x на една дума на ред, като замените интервалите


с нови редове:

'
ехо $x |tr -s ' '\н'

Резултатът е тук:

ghi
abc
Ghi
123
#def5
123z

Втората стъпка е да извикате old с регулярния израз ^[a-zA-Z]+, който отговаря на


всеки низ, състоящ се от една или повече главни и малки букви (и нищо друго).
Обърнете внимание, че превключвателят -E е необходим за анализиране на този вид
регулярен израз в sed, тъй като той използва част от по-новия/модерен синтаксис на
регулярен израз, недостъпен, когато sed беше нов.

'
ехо $x |tr -s ' '\n' |sed -nE "s/(^[a-zA-Z][a-zA-Z]*$)/\1/p"

Резултатът е тук:

ghi
abc
Ghi
Machine Translated by Google

Ако също така искате да сортирате изхода и да отпечатате само уникалните думи, прекарайте
резултатът от командите sort и uniq :

'
ехо $x |tr -s ' '\n' |sed -nE "s/(^[a-zA-Z][a-zA-
Z]*$)/\1/p"|сортирай|уник

Резултатът е тук:

Ghi
абв
ghi

Ако искате да извлечете само целите числа в променливата x, използвайте това


команда:

'
echo $x |tr -s |sort|uniq ' '\n' |sed -nE "s/(^[0-9][0-9]*$)/\1/p"

Резултатът е тук:

123

Ако искате да извлечете буквено-цифрови думи от променливата x, използвайте това


команда:

'
ехо $x |tr -s ' '\n' |sed -nE "s/(^[0-9a-zA-Z][0-9a-zA-
Z]*$)/\1/p"|сортирай|уник

Резултатът е тук:

123
123z
Ghi
абв
ghi

Сега можете да замените echo $x с набор от данни, за да извлечете само азбучен ред
низове от този набор от данни.

ЕДНОРЕДОВИ SED КОМАНДИ


Machine Translated by Google

Този раздел има за цел да покаже полезни проблеми, които можете да разрешите с един
ред на sed, и да ви изложи на още повече превключватели и аргументи, които можете да
смесвате и съчетавате, за да решавате свързани задачи.
Освен това sed поддържа други опции (които са извън обхвата на тази книга) за
изпълнение на много други задачи, някои от които са сложни и съответно сложни. Ако
срещнете нещо, което нито един от примерите в тази глава не покрива, но изглежда като
нещо, което sed може да направи, шансовете да го направи са добри. Търсене в интернет по
линия на „как да направя <xxx> в sed“ вероятно ще ви насочи в правилната посока или поне
към алтернативна команда bash, която ще бъде полезна.

Списък 6.7 показва съдържанието на data4.txt , споменат в някои от командите sed в


този раздел. Имайте предвид, че някои примери съдържат опции, които не са били
обсъждани по-рано в тази глава. Те са включени в случай, че имате нужда от желаната
функционалност (и можете да намерите повече подробности, като прочетете онлайн уроци).

ОБЯВКА 6.7. data4.txt

здравей свят4
здравей свят5 две
здравей свят6 три
здравей свят4 четири
ред пет
ред шест
ред седми

Отпечатайте първия ред на data4.txt с тази команда:

sed q < data4.txt

Резултатът е тук:

здравей свят3

Отпечатайте първите три реда на data4.txt с тази команда:

sed 3q < data4.txt

Резултатът е тук:

здравей свят4
Machine Translated by Google

здравей свят5 две


здравей свят6 три

Отпечатайте последния ред на data4.txt с тази команда:

sed '$!d' < data4.txt

Резултатът е тук:

ред седми

Можете също да използвате този фрагмент, за да отпечатате последния ред:

sed -n '$p' < data4.txt

Отпечатайте последните два реда на data4.txt с тази команда:

sed '$!N;$!D' <data4.txt

Резултатът е тук:

ред шест ред


седем

Отпечатайте редовете на data4.txt , които не съдържат world с тази команда:

sed '/свят/d' < data4.txt

Резултатът е тук:

ред пет
ред шест ред
седем

Отпечатайте дубликати на редовете в data4.txt , които съдържат думата world с тази


команда:

sed '/свят/p' < data4.txt

Резултатът е тук:

здравей свят4
здравей свят4
Machine Translated by Google

здравей свят5 две


здравей свят5 две
здравей свят6 три здравей свят6
три
здравей свят4 четири
здравей свят4 четири
ред пет ред шест
ред седем

Отпечатайте петия ред на data4.txt с тази команда:

sed -n '5p' < data4.txt

Резултатът е тук:

ред пет

Отпечатайте съдържанието на data4.txt и дублирайте ред пет с тази команда:

sed '5p' < data4.txt

Резултатът е тук:

здравей свят4 здравей


свят5 две
здравей свят6 три
здравей свят4 четири ред пет
ред пет

ред шест
ред седми

Отпечатайте редове от четири до шест на data4.txt с тази команда:

sed -n '4,6p' < data4.txt

Резултатът е тук:

здравей свят4 четири


ред пет
ред шест

Изтрийте редове от четири до шест от data4.txt с тази команда:


Machine Translated by Google

sed '4,6d' < data4.txt

Резултатът е тук:

здравей свят4
здравей свят5 две
здравей свят6 три
ред седми

Изтрийте секцията от редове между world6 и six в data4.txt с това


команда:

sed '/свят6/,/шест/d' < data4.txt

Резултатът е тук:

здравей свят4
здравей свят5 два реда седем

Отпечатайте частта от редове между свят6 и шест на data4.txt с това


команда:

sed -n '/свят6/,/шест/p' < data4.txt

Резултатът е тук:

здравей свят6 три


здравей свят4 четири ред пет
ред шест

Отпечатайте съдържанието на data4.txt и дублирайте частта от редове между тях


свят6 и шест с тази команда:

sed '/свят6/,/шест/p' < data4.txt

Резултатът е тук:

здравей свят4
здравей свят5 две
здравей свят6 три здравей свят6
три
Machine Translated by Google

здравей свят4 четири


здравей свят4 четири
ред пет ред
пет ред шест

ред шест
ред седми

Изтрийте четните редове в data4.txt с тази команда:

sed 'n;d;' <данни4.txt

Резултатът е тук:

здравей свят4
здравей свят6 три
ред пет ред
седем

Заменете буквите от a до m с „,“ с тази команда:

sed "s/[am]/,/g" <data4.txt

Резултатът е тук:

,,,,o wor,,4 ,,,,o


wor,,5 две ,,,,o wor,,6
t,r,, ,,,,o wor,,4 ,наш

,,n, ,,v,
,,n, s,x
,,n, s,v,n

Заменете буквите от a до m със знаците „,@#“ с тази команда:

sed "s/[am]/,@#/g" <data4.txt

Резултатът е тук:

,@#,@#,@#,@#o
wor,@#,@#4 ,@#,@#,@#,@#o wor,@#,@#5
две ,@#,@#, @#,@#o wor,@#,@#6 t,@#r,@#,@#
,@#,@#,@#,@#o wor,@#,@#4 ,@#нашите
,@#,@#n,@# ,@#,@#v,@# ,@#,@#n,@#
s,@#x ,@#,@#n,@#
s,@#v ,@#н
Machine Translated by Google

Командата sed не разпознава изходни последователности като \t, което означава,


че трябва буквално да вмъкнете раздел на вашата конзола. В случай на bash shell,
въведете контролния знак ^V и след това натиснете клавиша <TAB> , за да вмъкнете
знак <TAB> .
Изтрийте табулаторите в data4.txt с тази команда:

sed 's/ //g' <data4.txt

Резултатът е тук:

здравей свят4
здравей свят5 две
здравей свят6 три
здравей свят4 четири
ред пет
ред шест ред
седем

Изтрийте табулаторите и празните интервали в data4.txt с тази команда:

sed 's/ //g' <data4.txt

Резултатът е тук:

здравей свят4
helloworld5two
helloworld6three
helloworld4four linefive

линия шеста
линия седем

Заменете всеки ред от data4.txt с думата паста с тази команда:

sed 's/.*/\pasta/' < data4.txt

Резултатът е тук:

макаронени

изделия

макаронени

изделия

макаронени изделия паста паста


Machine Translated by Google

тестени

изделия паста

Вмъкнете два празни реда след третия ред и един празен ред след петия
ред в data4.txt с тази команда:

sed '3G;3G;5G' < data4.txt

Резултатът е тук:

здравей свят4
здравей свят5 две
здравей свят6 три

здравей свят4 четири ред пет

ред шест
ред седми

Вмъкнете празен ред след всеки ред на data4.txt с тази команда:

sed G < data4.txt

Резултатът е тук:

здравей свят4

здравей свят5 две

здравей свят6 три

здравей свят4 четири

ред пет

ред шест

ред седми

Вмъкнете празен ред след всеки друг ред на data4.txt с тази команда:

sed n\;G < data4.txt

Резултатът е тук:
Machine Translated by Google

здравей свят4
здравей свят5 две

здравей свят6 три


здравей свят4 четири

ред пет ред шест

ред седми

Обърнете редовете в data4.txt с тази команда:

сед '1! G; h;$!d' < data4.txt

Резултатът от предходната команда sed е тук:

ред седми
ред шест
ред пет
здравей свят4 четири
здравей свят6 три
здравей свят5 две
здравей свят4

РЕЗЮМЕ

Тази глава ви запозна с помощната програма sed , илюстрирайки основните задачи на


трансформацията на данни: позволяване на добавяне, премахване и мутация на данни
чрез съпоставяне на индивидуални шаблони или съпоставяне на позицията на редовете
във файл или комбинация от двете.

Освен това показахме, че sed не само използва регулярни изрази за съпоставяне


на данни, подобно на командата grep , но също така може да използва регулярни
изрази, за да опише как да трансформира данните. Накрая имаше списък с примери,
показващи както гъвкавостта на командата sed , така и, надявайки се, предаващи
усещането, че това е дори по-гъвкава и мощна помощна програма, отколкото можем
да покажем в една глава.
Machine Translated by Google

ГЛАВА 7

ПРАВЕЩЕ ВСИЧКО ОСТАНАЛО С awk

T
неговата глава ви запознава с командата awk , която е многофункционална програма
за манипулиране на данни и преструктуриране на набори от данни. Всъщност тази
помощна програма е толкова гъвкава, че са написани цели книги за помощната
програма awk . Awk е по същество цял програмен език в една команда, която приема
стандартен вход, дава стандартен изход и използва регулярни изрази и метасимволи по
същия начин, както другите команди на Unix. Това ви позволява да го включите в други
изрази и да правите почти всичко, с цената на добавяне на сложност към команден низ,
който може вече да прави доста неща.
Полезно е да добавите коментар, когато използвате awk, защото е толкова гъвкав, че
няма да е ясно коя от многото функции използвате с един поглед.
Първата част на тази глава предоставя кратко представяне на командата awk . Ще
научите за някои вградени променливи за awk и също как да манипулирате низови
променливи с помощта на awk. Обърнете внимание, че някои от тези примери,
свързани с низове, могат да се обработват и с помощта на други bash команди.
Втората част на тази глава ви показва условна логика, цикли while и for цикли в awk
за манипулиране на редовете и колоните в наборите от данни. Този раздел ви показва
как да изтривате редове и да обединявате редове в набори от данни, както и как да
отпечатвате съдържанието на файл като един ред текст. Ще видите как да „съединявате“
линии и групи от линии в набори от данни.
Третият раздел съдържа примерни кодове, които включват метасимволи (въведени
в Глава 1) и набори от знаци в awk команди. Ще видите също как да използвате условна
логика в awk команди, за да определите дали да отпечатате ред текст.
Machine Translated by Google

Четвъртият раздел илюстрира как да разделите текстов низ, който съдържа множество „.“
символи като разделител, последвани от примери за awk за извършване на числени изчисления
(като добавяне, изваждане, умножение и деление) във файлове, съдържащи числови данни. Този
раздел също ви показва различни цифрови функции, които са налични в awk, както и как да
отпечатате текст във фиксиран набор от колони.

Петият раздел обяснява как да подравните колони в набор от данни и как да подравните и
обедините колони в набор от данни. Ще видите как да изтривате колони, как да избирате поднабор
от колони от набор от данни и как да работите с многоредови записи в набори от данни. Този
раздел съдържа някои едноредови awk команди, които могат да бъдат полезни за манипулиране

на съдържанието на набори от данни.


Последният раздел на тази глава съдържа двойка случаи на употреба, включващи вложени
кавички и формати на дата в набори от структурирани данни.

КОМАНДАТА AWK

Командата awk (Aho, Weinberger и Kernighan) има синтаксис, подобен на C, и можете да


използвате тази програма за извършване на много сложни операции с числа и текстови низове .

Като страничен коментар, има и командата gawk , която е GNU awk, както и командата nawk е
„нова“ awk (нито една команда не се обсъжда в тази книга). Едно предимство на nawk е, че ви
позволява да задавате външно стойността на вътрешна променлива.

Вградени променливи, които контролират awk

Командата awk предоставя променливи, които можете да промените от техните


стойности по подразбиране, за да контролирате как awk изпълнява операции. Примери
за такива променливи (и техните стойности по подразбиране) включват FS (" "), RS ("\n"),
ORS ("\n") OFS (" "), SUBSEP и IGNORECASE . Променливите FS и RS указват разделителя
на полето и разделителя на записа, докато променливите OFS и ORS указват съответно
разделителя на изходното поле и разделителя на изходния запис.

Можете да мислите за разделителите на полета като за разделители/IFS, които използвахме в


други команди по-рано. Разделителите на записи се държат по начин, подобен на начина, по който
sed третира отделните редове; например sed може да съпостави или изтрие диапазон от редове,
вместо да съпостави или изтрие нещо, което съответства на регулярен израз
Machine Translated by Google

(и разделителят на записи на awk по подразбиране е знакът за нов ред, така че по


подразбиране awk и sed имат подобна способност да манипулират и препращат към редове
в текстов файл).
Като прост пример, можете да отпечатате празен ред след всеки ред от файл , като
промените ORS от един нов ред по подразбиране на два нови реда, както е показано тук:

котешки колони.txt | awk 'BEGIN { ORS ="\n\n" } ; { print $0 }'

Други вградени променливи включват FILENAME (името на файла, който awk чете в
момента), FNR (номерът на текущия запис в текущия файл), NF (броят на полетата в текущия
входен запис) и NR (номерът на входните записи, обработени от awk от началото на
изпълнението на програмата).
Консултирайте се с онлайн документацията за допълнителна информация относно
тези (и други) аргументи за командата awk .

Как работи командата awk?

Командата awk чете входните файлове един запис наведнъж (по подразбиране един
запис е един ред). Ако даден запис съвпада с шаблон, тогава се извършва действие (в
противен случай не се извършва действие). Ако шаблонът за търсене не е даден, тогава awk
изпълнява дадените действия за всеки запис на входа. Поведението по подразбиране, ако
не е дадено действие, е да се отпечатат всички записи, които отговарят на дадения шаблон.
И накрая, празните скоби без никакво действие не правят нищо; т.е. няма да изпълни
операцията за печат по подразбиране. Обърнете внимание, че всеки израз в действията
трябва да бъде разделен с точка и запетая.
За да направите предходния параграф по-ясен, ето няколко прости примера, включващи
текстови низове и командата awk (резултатите се показват след всеки кодов фрагмент).
Превключвателят -F задава разделителя на полето на всичко, което го следва, в този случай
интервал. Превключвателите често предоставят пряк път към действие, което обикновено
се нуждае от команда в блок BEGIN{} :

x="abcd e" echo $x |awk

-F" " '{print $1}'


а

echo $x |awk -F" " '{print NF}' 5 echo $x |awk -F" " '{print $0}'

abcde
Machine Translated by Google

echo $x |awk -F" " '{print $3, $1}'


ок

Нека променим FS (разделител на запис) на празен низ, за да изчислим


дължина на низ, този път използвайки синтаксиса BEGIN{} :

ехо "abc" | awk 'НАЧАЛО { FS = 3 "" }; { print NF }'

Следващият пример илюстрира няколко еквивалентни начина за указване на test.txt


като входен файл за awk команда:

awk < test.txt '{ print $1 }' awk '{ print $1 }' <
test.txt awk '{ print $1 }' test.txt

Тук е показан още един начин (но както обсъдихме по-рано, може
бъдете неефективни, така че го правете само ако котката добавя стойност по някакъв начин):

котешки тест.txt | awk '{ print $1 }'

Този прост пример за четири начина за извършване на една и съща задача трябва
да илюстрира защо включването на изрази за коментар в awk команди с всякаква
сложност е почти винаги добра идея. Имайте предвид, че следващият човек, който ще
разгледа вашия код, може да не знае, че е запознат с вашия стил на кодиране.

ПОДРАВНЯВАНЕ НА ТЕКСТ С КОМАНДАТА printf().

Тъй като awk е език за програмиране в една команда, той също


има свой собствен начин за създаване на форматиран изход чрез командата printf() .
Списък 7.1 показва съдържанието на columns2.txt, а листинг 7.2 показва
съдържанието на шел скрипта AlignColumns1.sh , който ви показва как да подравните
колоните в текстов файл.

ОБЯВКА 7.1: columns2.txt

едно две
три четири
едно две три четири пет
шест
едно две три
четири пет
Machine Translated by Google

ЛИСТИНГ 7.2: AlignColumns1.sh

awk
{
# ляво подравняване $1 в колона от 10 знака
# дясно подравняване $2 в колона от 10 знака
# дясно подравняване $3 в колона от 10 знака
# дясно подравняване $4 в колона от 10 знака
printf("%-10s*%10s*%10s*%10s*\n", $1, $2, $3, $4)
}
' columns2.txt

Листинг 7.2 съдържа оператор printf() , който показва първите четири полета
на всеки ред във файла columns2.txt, където всяко поле е с ширина 10 знака.
Резултатът от стартирането на кода в листинг 7.2 е тук:

един * две* * *

три * четири* * *

един * две* три* четири*


пет * шест* * *

един * две* три* *

четири * пет* * *

Имайте предвид, че printf е сравнително мощен и като такъв има своето


собствен синтаксис, който е извън обхвата на тази глава. Едно търсене онлайн може
намерете страниците с ръководство, както и дискусии за „как да направите X с printf().“

УСЛОВНА ЛОГИКА И КОНТРОЛНИ ИЗЯВЛЕНИЯ

Подобно на други езици за програмиране, awk осигурява поддръжка за условни


логика (if/else) и команди за управление (for/while цикли). awk е единственият начин
за поставяне на условна логика в поток от команди, без да се създава,
инсталиране и добавяне към пътя на персонализиран изпълним shell скрипт. The
следният кодов блок ви показва как да използвате логиката if/else :

ехо "" | awk


НАЧАЛО {x = 10}
{
ако (x % 2 == 0)}
отпечатайте "x е четно"
}
иначе }
отпечатайте "x е странно"
}
Machine Translated by Google

}
'

Предходният кодов блок инициализира променливата x със стойност 10 и


отпечатва „x е четно“, ако x се дели на 2, в противен случай отпечатва „x е нечетно“.

Изявлението while

Следният кодов блок илюстрира как да използвате цикъл while в awk:

ехо "" | awk ' {

x=0
while(x < 4) { print "x:",xx =
x+1

}
}
'

Предходният кодов блок генерира следния изход:

х:0
х:1
х:2
х:3

Следният кодов блок илюстрира как да използвате do while цикъл в awk:

ехо "" | awk ' {

x = 0 do
{ print
"x:", xx = x + 1

} докато (x < 4)
}
'

Предходният кодов блок генерира следния изход:

х:0
х:1
х:2
х:3
Machine Translated by Google

Цикъл for в awk

Листинг 7.3 показва съдържанието на Loop.sh , което илюстрира как да отпечатате


списък с числа в цикъл. Обърнете внимание, че i++ е друг начин за писане на i=i+1 в
awk (и повечето езици, получени от C).

ЛИСТИНГ 7.3: Loop.sh

ехо "" | awk


НАЧАЛО {}
{
for(i=0; i<5; i++) { printf("%3d", i)

}
}
КРАЙ { печат "\n" }
'

Листинг 7.3 съдържа for цикъл, който отпечатва числа на един и същ ред чрез
командата printf() . Забележете, че нов ред се отпечатва само в блока END на кода.
Резултатът от листинг 7.3 е тук:

01234

Цикъл for с команда за прекъсване

Следният кодов блок илюстрира как да използвате израз за прекъсване в for цикъл
в awk:

ехо "" | awk ' {

for(x=1; x<4; x++) { print "x:",x if(x


== 2) { break;

}
}
}
'

Предходният кодов блок отпечатва изход само докато променливата x има


стойност 2, след което цикълът излиза (поради прекъсването в условната логика).
Показва се следният изход:
Machine Translated by Google

х:1
X:2

Следващият и продължете изявления

Следният кодов фрагмент илюстрира как да използвате next и продължите в for цикъл в awk:

awk
{
/израз1/ { var1 = 5; следващ } /израз2/ { var2 = 7;
next } /expression3/ { continue } // някой друг кодов
блок тук

' някакъв файл

Когато текущият ред съвпада с израз1, тогава на var1 се присвоява стойност 5 и awk
чете следващия входен ред; следователно израз2 и израз3 няма да бъдат тествани. Ако
израз1 не съвпада и израз2 съвпада , тогава на var2 се присвоява стойност 7 и след това
awk ще прочете следващия входен ред. Ако само израз3 води до положително съвпадение,
тогава awk пропуска оставащия блок код и обработва следващия входен ред.

ИЗТРИВАНЕ НА АЛТЕРНАТИВНИ РЕДОВЕ В НАБОРА ДАННИ

Списък 7.4 показва съдържанието на linepairs.csv, а листинг 7.5 показва съдържанието


на deletelines.sh , илюстрирайки как да отпечатате редуващи се редове от набора от
данни linepairs.csv , които имат точно две колони.

ЛИСТИНГ 7.4: linepairs.csv

a,b,c,de,f,g,h
1,2,3,4
5,6,7,8

ОБЯВКА 7.5: deletelines.sh

inputfile="linepairs.csv"
outputfile="linepairsdeleted.csv"
Machine Translated by Google

awk ' NR%2 {printf "%s", $0; печат ""; следващ}' < $inputfile > $outputfile

Листинг 7.5 проверява дали текущият номер на запис NR се дели на 2, в който случай
отпечатва текущия ред и пропуска следващия ред в набора от данни. Изходът се
пренасочва към посочения изходен файл, чието съдържание е тук:

a,b,c,d 1,2,3,4

Една малко по-често срещана задача включва сливането на последователни редове,


което е темата на следващия раздел.

СЛИВАНЕ НА ЛИНИИ В НАБОРИ ДАННИ

Листинг 7.6 показва съдържанието на columns.txt, а листинг 7.7 показва съдържанието


на ColumnCount1.sh , илюстрирайки как да отпечатате редовете от текстовия файл
columns.txt , които имат точно две колони.

ОБЯВКА 7.6: columns.txt

едно две три


едно две
едно две три четири
един
едно три
едно четири

ОБЯВКА 7.7: ColumnCount1.sh

awk
{
if( NF == 2 ) { print $0 }

} ' columns.txt

Листинг 7.7 е ясен: ако текущият номер на запис е четен, тогава текущият ред се
отпечатва (т.е. редовете с нечетни номера се пропускат). Резултатът от стартирането на
кода в листинг 7.7 е тук:

едно две
Machine Translated by Google

едно три
едно четири

Ако искате да покажете редовете, които не съдържат 2 колони, използвайте следния кодов
фрагмент:

if( NF != 2 ) { print $0 }

Отпечатване на съдържанието на файла като един ред

Съдържанието на test4.txt е тук (обърнете внимание на празните редове):

абв

деф

абв

абв

Следният кодов фрагмент илюстрира как да отпечатате съдържанието на

test4.txt като един ред:

awk '{printf("%s", $0)}' test4.txt

Резултатът от предходния кодов фрагмент е тук. Вижте дали можете да кажете какво
се случва, преди да прочетете обяснението в следващия параграф:

Abcdefabcabc

Обяснение: %s е синтаксисът на разделителя на записи за printf(), а дясната кавичка показва, че


низът е последван от празното поле „“. Обратно, „%s“ показва, че се показва бяло пространство след
показване на съдържанието на %s.

Нашият разделител на записи по подразбиране за awk е /n (нов ред); това, което printf() прави, е
премахване на всички нови редове. Празните редове ще изчезнат напълно, тъй като всичко, което
имат, е новият ред, така че резултатът е, че всеки действителен текст ще бъде обединен без нищо
между тях. Ако бяхме добавили интервал между %s и крайната кавичка, щеше да има интервал между
всеки символен блок плюс допълнителен интервал за всеки нов ред.
Machine Translated by Google

Забележете как следният коментар помага за разбирането на кодовия фрагмент:

# Обединяване на целия текст в един ред чрез премахване на новия ред awk '{printf("%s", $0)}' test4.txt

Свързване на групи от редове в текстов файл

Листинг 7.8 показва съдържанието на digits.txt, а листинг 7.9 показва съдържанието на


digits.sh , който „съединява“ три последователни реда текст във файла digits.txt.

ОБЯВКА 7.8: digits.txt

1
2

5
6
7
8
9

ЛИСТИНГ 7.9: digits.sh

awk -F" " '{ printf("%d",


$0) if(NR % 3 == 0)
{ printf("\n") } }' digits.txt

Листинг 7.9 отпечатва три последователни реда текст на един и същи ред, след което се отпечатва
нов ред. Това има ефект на „свързване“ на всеки три последователни реда текст. Резултатът от
стартирането на digits.sh е тук:

123
456
789

Съединяване на алтернативни редове в текстов файл


Machine Translated by Google

Списък 7.10 показва съдържанието на columns2.txt, а листинг 7.11 показва


съдържанието на JoinLines.sh , който „съединява“ два последователни реда текст във файла
columns2.txt.

ОБЯВКА 7.10: columns2.txt

едно две
три четири
едно две три четири пет шест
едно две три

четири пет

ОБЯВКА 7.11: JoinLines.sh

awk
{
printf("%s",$0) if( $1 !~ /
one/) { print " " }

} ' columns2.txt

Резултатът от стартирането на листинг 7.11 е тук:

едно две три четири едно две


три четири пет шест
едно две три четири пет

Забележете, че кодът в листинг 7.11 зависи от присъствието на низа „едно“ като първо
поле в редуващи се редове от текст - ние сливаме въз основа на съвпадение на прост
шаблон, вместо да го обвързваме с комбинации от записи.
За да обедините всяка двойка редове вместо сливане въз основа на съвпадение на
шаблон, използвайте модифицирания код в листинг 7.12.

ЛИСТИНГ 7.12: JoinLines2.sh

awk
НАЧАЛО { брой = 0 } {

printf("%s",$0) if( ++count


% 2 == 0) { print " " } } columns2.txt
Machine Translated by Google

Още един начин за „съединяване“ на последователни редове е показан в листинг


7.13, където входният файл и изходният файл се отнасят за файлове, които можете да
попълните с данни. Това е още един пример за awk команда, която може да бъде
пъзел, ако се срещне в програма без коментар. Прави абсолютно същото като листинг
7.12, но целта му е по-малко очевидна поради по-компактния синтаксис.

ЛИСТИНГ 7.13: JoinLines3.sh

inputfile="linepairs.csv"
outputfile="linepairsjoined.csv" awk ' NR%2 {printf "%s,", $0;
следващ;}1' < $inputfile > $outputfile

СЪОТВЕТСТВИЕ С МЕТАХАРАКТЕРИ И НАБОРИ ЗНАЦИ

Ако можем да съпоставим прост шаблон, вероятно вече очаквате, че можете да


съпоставите и регулярен израз, точно както направихме в и sed. Листинг 7.14 показва
grep

съдържанието на Patterns1.sh , което използва метасимволи, за да съответства на


началото и края на ред текст във файла columns2.txt.

ЛИСТИНГ 7.14: Patterns1.sh

awk ' /^f/


{ print $1 } /two $/ { print $1 } '
columns2.txt

Резултатът от стартирането на листинг 7.14 е тук:

един
пет
четири

Листинг 7.15 показва съдържанието на RemoveColumns.txt с редове, които


съдържат различен брой колони.

ОБЯВКА 7.15: columns3.txt

123 едно две


456 три четири
едно две три четири пет 123 шест
Machine Translated by Google

едно две три


четири пет

Списък 7.16 показва съдържанието на MatchAlpha1.sh , което съответства на


текстови редове, които започват с азбучни знаци, както и редове, които съдържат
цифрови низове във втората колона.

ОБЯВКА 7.16: MatchAlpha1.sh

awk
{
if( $0 ~ /^[0-9]/) { print $0 } if( $0 ~ /^[az]+ [0-9]/) { print $0 }

} ' columns3.txt

Резултатът от листинг 7.16 е тук:

123 едно две


456 три четири
пет 123 шест

ПЕЧАТ НА РЕДОВЕ С ИЗПОЛЗВАНЕ НА УСЛОВНА ЛОГИКА

Листинг 7.17 показва съдържанието на products.txt , който съдържа три


колони с информация.

ОБЯВКА 7.17: products.txt

Мобилен телефон 400 нови 300 нови


Таблет 300 използвани
Таблет
Използван мобилен телефон 200
Използван мобилен телефон 100 бр

Следният кодов фрагмент отпечатва редовете текст в products.txt , чиято втора колона е по-голяма от 300:

awk '$2 > 300' products.txt

Резултатът от предходния кодов фрагмент е тук:

Мобилен телефон 400 нов


Machine Translated by Google

Следният кодов фрагмент отпечатва редовете с текст в products.txt , чийто


продукт е нов:

awk '($3 == "ново")' products.txt

Резултатът от предходния кодов фрагмент е тук:

Мобилен телефон 400 нов


Таблет 300 нови

Следният кодов фрагмент отпечатва първата и третата колона на редовете


текст в products.txt , чиято цена е равна на 300:

awk ' $2 == 300 { print $1, $3 }' products.txt

Резултатът от предходния кодов фрагмент е тук:

Таблет нов
Използван таблет

Следният кодов фрагмент отпечатва първата и третата колона на редовете


текст в products.txt , които започват с низа Tablet:

awk '/^Tablet/ { print $1, $3 }' products.txt

Резултатът от предходния кодов фрагмент е тук:

Таблет нов
Използван таблет

РАЗДЕЛЯНЕ НА ИМЕНА НА ФАЙЛОВЕ С AWK

Списък 7.18 показва съдържанието на SplitFilename2.sh , което илюстрира как


да се раздели име на файл, съдържащ „.“ знак за увеличаване на числовата
стойност на един от компонентите на името на файла. Имайте предвид, че този код
работи само за име на файл с точно очаквания синтаксис. Възможно е да напишете
по-сложен код, за да преброите броя на сегментите, или като алтернатива просто
да кажете „променете полето точно преди .zip“, което ще изисква само името на
файла да има формат, съответстващ на последните две секции (<anystructure>.number .zip).
Machine Translated by Google

ЛИСТИНГ 7.18: SplitFilename2.sh

'
ехо "05.20.144q.az.1.zip" | awk -F"." {

f5=$5 + 1
printf("%s.%s.%s.%s.%s.%s",$1,$2,$3,$4,f5,$6) }'

Резултатът от листинг 7.18 е тук:

05.20.144q.az.2.zip

РАБОТА С АРИТМЕТИЧНИ ОПЕРАТОРИ POSTFIX

Списък 7.19 показва съдържанието на mixednumbers.txt , който съдържа postfix


оператори, което означава числа, където отрицателният (или положителният) знак се
появява в края на стойността на колона вместо в началото на числото.

ОБЯВКА 7.19: mixednumbers.txt

324 000-|10|983 000- 453 000-|30|


298 000- 783 000-|20|347 000-

Списък 7.20 показва съдържанието на AddSubtract1.sh , което илюстрира как да


добавите редовете с числа в листинг 7.19.

ЛИСТИНГ 7.20: AddSubtract1.sh

myFile="mixednumbers.txt"

awk -F"|" '


НАЧАЛО { ред = 0; общо = 0} {

split($1, arr, "-") f1 = arr[1] if($1 ~ /-/)


{ f1 = -f1 } линия +=
f1 split($2, arr, "-") f2 = arr[1] if($2 ~ /-/) { f2 = -f2 }
линия += f2

split($3, arr, "-")


Machine Translated by Google

f3 = arr[1] if($3 ~ /-/)


{f3 = -f3 } линия += f3

printf("f1: %d f2: %d f3: %d ред: %d\n",f1,f2,f3, ред) общо += ред

линия = 0
}
КРАЙ { print "Total: ",total } ' $myfile

Резултатът от листинг 7.20 е тук. Вижте дали можете да разберете какво прави
кодът, преди да прочетете обяснението, което следва:

f1: -324 f2: 10 f3: -983 линия: -1297


f1: -453 f2: 30 f3: -298 ред: -721 f1: -783 f2: 20 f3: -347 ред: -1110

Общо: -3128

Кодът предполага, че знаем формата на файла. Функцията split() превръща всеки


запис на поле във вектор с дължина две, като първата позиция е равна на число, а
втората позиция е или празна стойност, или тире; след това улавя първия номер на
позиция в променлива. Операторът if проверява дали оригиналното поле има тире в
него. Ако полето има тире, тогава числовата променлива се прави отрицателна, в
противен случай остава сама. След това добавя реда.

ЧИСЛОВИ ФУНКЦИИ В AWK

Функцията int(x) връща целочислената част от числото. Ако числото вече не е


цяло число, то попада между две цели числа. От двете възможни цели числа
функцията ще върне най-близкото до нула. Това е различно от функцията за
закръгляване, която избира по-близкото цяло число.
Например int(3) е 3, int(3.9) е 3, int(-3.9) е -3 и int(-3) е -3
както добре. Пример за функцията int(x) в awk команда е тук:

awk 'BEGIN { print


int(3.534); печат int(4); печат
int(-5.223); печат int(-5);

}'

Резултатът е тук:
Machine Translated by Google

3
4
-5
-5

Функцията exp(x) ви дава експоненциала на x или съобщава за грешка, ако x е


извън диапазона. Диапазонът от стойности x , които можете да имате, зависи от
представянето с плаваща запетая на вашата машина.

awk 'BEGIN{ print


exp(123434346); печат exp(0);
печат exp(-12);

}'

Резултатът е тук:

инф
1
6.14421e-06

Функцията log(x) ви дава естествения логаритъм на в противенх, ако x е положителен;


случай тя съобщава за грешка (inf означава безкрайност, а nan „не в изходните средства
е число“).

awk 'BEGIN{ print


log(12); печат на
дневник (0); печат
на дневник (1);
дневник за печат (-1); }'

Резултатът е тук:

2.48491 -инф

0
нан

Функцията sin (x) ви дава синус от x , а cos(x) ви дава косинус от


х, с x в радиани:

awk 'BEGIN { print


cos(90);
Machine Translated by Google

печат cos(45);
}'

Резултатът е тук:

-0,448074
0,525322

Функцията rand() ви дава произволно число. Стойностите на rand() са равномерно


разпределени между 0 и 1: стойността никога не е 0 и никога 1.
Често искате вместо това произволни цели числа. Ето дефинирана от потребителя функция
можете да използвате, за да получите произволно неотрицателно цяло число, по-малко от n:

функция randint(n) { return int(n *


rand())
}

Продуктът произвежда произволно реално число, по-голямо от 0 и по-


малко от n. След това го правим цяло число (използвайки int) между 0 и n - 1.
Ето пример, при който подобна функция се използва за генериране на случаен принцип
цели числа между 1 и n:

ехо "" | awk


# Функция за хвърляне на симулиран зар. функция roll(n)
{ return 1 + int(rand() * n)}
# Хвърлете 3 шестстранни зара и отпечатайте общия брой точки. {

printf("%d точки\n", roll(6)+roll(6)+roll(6))


}'

Обърнете внимание, че rand започва да генерира числа от една и съща точка (или
„seed“) при всяко извикване на awk . Следователно програмата ще дава едни и същи
резултати при всяко стартиране. Ако искате една програма да прави различни неща всеки
път, когато се използва, трябва да промените семето на стойност, която ще бъде различна
при всяко изпълнение.

Използвайте функцията srand(x) , за да зададете началната точка или началната точка за


генериране на произволни числа на стойността x. Всяка начална стойност води до определена
последователност от „случайни“ числа. По този начин, ако зададете началната стойност на
същата стойност втори път, ще получите отново същата последователност от „случайни“ числа.
Ако пропуснете аргумента x, както в srand(), тогава текущата дата и час от деня се използват за
семе. Ето как да получите произволни числа, които са наистина непредвидими. Върнатата
стойност на srand() е предишното начало. Това прави
Machine Translated by Google

лесно е да следите семената за използване при последователно възпроизвеждане на


последователности от произволни числа.
Функцията time() (не във всички версии на awk) връща текущия час в секунди от 1 януари
1970 г. Функцията ctime (не във всички версии на awk) приема числов аргумент в секунди и
връща низ, представляващ съответната дата , подходящи за печат или допълнителна
обработка.
Функцията sqrt(x) ви дава положителния корен квадратен от x. Отчита грешка,
ако x е отрицателно. Така sqrt(4) е 2.

ехо "" | awk 'BEGIN{ print sqrt(16);


печат sqrt(0); печат
sqrt(-12);

}'

Резултатът е тук:

нан

ЕДНОРЕДОВИ AWK КОМАНДИ

Кодовите фрагменти в този раздел препращат към текстовия файл short1.txt, който
можете да попълните с всякакви данни по ваш избор.
Следният кодов фрагмент отпечатва всеки ред, предшестван от броя на полетата във
всеки ред:

awk '{print NF ":" $0}' short1.txt

Отпечатайте най-дясното поле във всеки ред:

awk '{print $NF}' short1.txt

Отпечатайте редовете, които съдържат повече от 2 полета:

awk '{if(NF > 2) print }' short1.txt

Отпечатайте стойността на най-дясното поле, ако текущият ред съдържа повече от 2


полета:
Machine Translated by Google

awk '{if(NF > 2) print $NF }' short1.txt

Премахнете началните и крайните празни интервали:

ехо " абв " | awk '{gsub(/^[ \t]+|[ \t]+$/,"");print}'

Отпечатайте първото и третото поле в обратен ред за редовете, които съдържат поне 3 полета:

awk '{if(NF > 2) print $3, $1}' short1.txt Отпечатва редовете, които
съдържат низа one: awk '{if(/one/) print }' *txt

Както можете да видите от предходните кодови фрагменти, лесно е да извлечете информация


или подмножества от редове и колони от текстови файлове, като използвате проста условна логика
и вградени променливи в командата awk .

ПОЛЕЗНИ КРАТКИ AWK СКРИПТОВЕ

Този раздел съдържа набор от кратки awk -базирани скриптове за извършване на различни
операции. Някои от тези скриптове могат да се използват и в други шел скриптове за извършване
на по-сложни операции. Списък 7.21 показва съдържанието на файла data.txt , който се използва в
различни примерни кодове в този раздел.

ОБЯВКА 7.21: data.txt

това е първи ред, който съдържа повече от 40 знака, това е втори ред

това е ред три, който също съдържа повече от 40 знака

четири това е ред шест и предходният ред е празен

осми ред и предходният ред също е празен

Следният кодов фрагмент отпечатва всеки ред, който е по-дълъг от 40 знака:

awk 'length($0) > 40' data.txt

Сега отпечатайте дължината на най-дългия ред в data.txt:


Machine Translated by Google

awk '{ if (x < length()) x = length() }


END { print "максималната дължина на реда е " x }' < data.txt

Входът се обработва от помощната програма за разширяване , за да промени разделите в


интервали, така че сравнените ширини всъщност са колоните с дясно поле.
Отпечатайте всеки ред, който има поне едно поле:

awk 'NF > 0' data.txt

Предходният кодов фрагмент илюстрира лесен начин за изтриване на празни редове от файл
(или по-скоро за създаване на нов файл, подобен на стария файл, но от който празните редове са
премахнати).
Отпечатайте седем произволни числа от 0 до 100, включително:

awk 'BEGIN { for (i = 1; i <= 7; i++) print int(101 * rand()) }'

Пребройте редовете във файл:

awk 'END { print NR }' < data.txt

Отпечатайте четните редове във файла с данни:

awk 'NR % 2 == 0' data.txt

Ако използвате израза „NR % 2 == 1“ в предишния кодов фрагмент, програмата ще отпечата


нечетните редове.
Вмъкнете дубликат на всеки ред в текстов файл:

awk '{print $0, '\n', $0}' < data.txt

Вмъкнете дубликат на всеки ред в текстов файл и също така премахнете празните редове:

awk '{print $0, "\n", $0}' < data.txt | awk 'NF > 0'

Вмъкнете празен ред след всеки ред в текстов файл:

awk '{print $0, "\n"}' < data.txt

ОТПЕЧАТВАНЕ НА ДУМИТЕ В ТЕКСТОВ НИЗ В AWK


Machine Translated by Google

Листинг 7.22 показва съдържанието на Fields2.sh , което илюстрира как да


отпечатате думите в текстов низ с помощта на командата awk .

ЛИСТИНГ 7.22: Fields2.sh

ехо "abcd e"| awk ' {

за (i=1; i<=NF; i++) {


отпечатайте "Поле ",i,":",$i
}
}
'

Резултатът от листинг 7.22 е тук:

Поле 1: а
Поле 2: b
Поле 3: c
Поле 4: d
Поле 5: д

БРОЙ СЪВЕТЯНИЯ НА НИЗ В ОПРЕДЕЛЕНИ РЕДОВЕ

Списък 7.23 и листинг 7.24 показват съответно съдържанието на data1.csv и


data2.csv . Списък 7.25 показва съдържанието на checkrows.sh , което илюстрира
как да се преброи броят на срещанията на низа в колона 3 в редове 2, 5 и 7.

ОБЯВКА 7.23: data1.csv

в,миналото,или,настоящето
за,миналото,или,настоящето
в,миналото,или,настоящето
за,поставянето,или,бъдещето в,миналото,
или,настоящето напълно,несвързано,ред1
в,миналото,или,настоящето
напълно,несвързано,ред2

ОБЯВКА 7.24: data2.csv

в,миналото,или,настоящето,
напълно,несвързан,ред1
за,миналото,или,настоящето
Machine Translated by Google

напълно,несвързано,ред2
за,поставяне,или,бъдещето
в,миналото,или,настоящето
в,миналото,или,настоящето
напълно,несвързано,ред3

ЛИСТИНГ 7.25: checkrows.sh

files="`ls data*.csv| tr '\n' ' '`" echo "Списък с файлове:


$files" awk -F","
'

( FNR==2 || FNR==5 || FNR==7 ) ако ($3 ~


"минало") { { брой++ }
}
КРАЙ
{ printf "минало: съвпадение %d пъти (НЕТОЧНО) ", брой printf "в поле 3 в редове 2/5/7\n"

}' данни*.csv

Списък 7.25 търси срещания на миналия низ в колони 2, 5 и 7 поради следния кодов
фрагмент:

( FNR==2 || FNR==5 || FNR==7 )


ако ($3 ~ "минало") { { брой++ }
}

Ако възникне съвпадение, тогава стойността на броя се увеличава. Блокът END отчита броя пъти, в които

миналият низ е намерен в колони 2, 5 и 7. Имайте предвид, че низове като paste и pasted ще съвпадат с

миналия низ .
Резултатът от листинг 7.25 е тук:

Списък с файлове: data1.csv data2.csv минало: съответства 5

пъти (НЕТОЧНО) в поле 3 в редове 2/5/7

Shell скриптът checkrows2.sh замества термина $3 ~ "минало" с термина $3 ==


"минало" в checkrows.sh , за да провери за точни съвпадения, което произвежда следния
изход:

Списък с файлове: data1.csv data2.csv минало: съответства 4

пъти (ТОЧНО) в поле 3 в редове 2/5/7

ОТПЕЧАТВАНЕ НА НИЗ ВЪВ ФИКСИРАН БРОЙ КОЛОНИ


Machine Translated by Google

Листинг 7.26 показва съдържанието на FixedFieldCount1.sh , което илюстрира


как да отпечатате думите в текстов низ с помощта на командата awk .

ОБЯВКА 7.26: FixedFieldCount1.sh

echo "aa bb cc dd ee ff gg hh"| awk


НАЧАЛО {colCount = 3} {

for(i=1; i<=NF; i++) { printf("%s ", $i)


if(i % colCount == 0) { print
""

}
}
}
'

Резултатът от листинг 7.26 е тук:

aa bb cc
dd ee ff
gg hh

ПЕЧАТ НА НАБОР ДАННИ ВЪВ ФИКСИРАН БРОЙ КОЛОНИ

Листинг 7.27 показва съдържанието на VariableColumns.txt с редове текст, които


съдържат различен брой колони.

ЛИСТИНГ 7.27: VariableColumns.txt

това е ред едно това е


ред номер едно това е третият и
последен ред

Листинг 7.28 показва съдържанието на Fields3.sh , което илюстрира как да


отпечатате думите в текстов низ с помощта на командата awk .

ЛИСТИНГ 7.28: Fields3.sh

awk '{printf("%s ", $0)}' | awk


НАЧАЛО { columnCount = 3 } {

for(i=1; i<=NF; i++) { printf("%s ", $i)


Machine Translated by Google

if( i % columnCount == 0 ) print " "

}
}
' VariableColumns.txt

Резултатът от листинг 7.28 е тук:

това е линия
едно това е ред
номер едно това е

трети и последен
линия

ПОДРАВНЯВАНЕ НА КОЛОНИ В НАБОРА ДАННИ

Ако сте прочели предходните два примера, примерният код в този раздел е лесен за разбиране.
Ще научите как да подреждате отново колони с данни, които са правилни по отношение на тяхното
съдържание, но са поставени в различни редове (и следователно са неправилно подравнени). Списък
7.29 показва съдържанието на mixed-data.csv с неподравнени стойности на данните. В допълнение,
първият ред и последният ред в листинг 7.28 са празни редове, които ще бъдат премахнати от скрипта
на обвивката в този раздел.

ОБЯВКА 7.29: mixed-data.csv

Сара, Джоунс, 1000, Калифорния, Сали, Смит, 2000, Илинойс,


Дейв, Джоунс, 3000, Флорида, Джон, Джоунс,
4000, Калифорния,

Дейв, Джоунс, 5000, Ню Йорк, Майк,


Джоунс, 6000, Ню Йорк, Тони, Джоунс, 7000, Вашингтон

Листинг 7.30 показва съдържанието на mixed-data.sh , което илюстрира как да


пренастройте набора от данни в листинг 7.29.

ЛИСТИНГ 7.30: mixed-data.sh

#---------------------------------------- # 1) премахнете празните редове # 2 )


премахнете подаването на ред # 3)
отпечатайте LF след всяко четвърто
поле # 4) премахнете крайния ',' от всеки ред #------------------------
-----------------
Machine Translated by Google

inputfile="mixed-data.csv"

grep -v "^$" $inputfile |awk -F"," '{printf("%s",$0)}' | awk


НАЧАЛО { columnCount = 4 } {

for(i=1; i<=NF; i++) { printf("%s ", $i) if( i %


columnCount == 0) { print "" }

}
}' > временни колони

# 4) премахване на крайния ',' от изхода: cat temp-columns | sed 's/,


$//' | sed 's/ $//' > $outputfile

Листинг 7.30 започва с команда grep , която премахва празните редове, последвана
от команда awk , която отпечатва редовете на набора от данни като един ред текст.
Втората команда awk инициализира променливата columnCount със стойност 4 в
блока BEGIN , последвана от цикъл for в главния блок на кода за изпълнение, който
итерира през полетата за въвеждане. След като четири полета са отпечатани на един
и същи изходен ред, се отпечатва нов ред, което има ефект на преподреждане на
входния набор от данни като изходен набор от данни, състоящ се от редове, които
имат четири полета. Резултатът от листинг 7.30 е тук:

Сара, Джоунс, 1000, Калифорния


Сали, Смит, 2000 г., Илинойс
Дейв, Джоунс, 3000, Флорида
Джон, Джоунс, 4000, Калифорния
Дейв, Джоунс, 5000, Ню Йорк
Майк, Джоунс, 6000, Ню Йорк
Тони, Джоунс, 7000, Вашингтон

ПОДРАВНЯВАНЕ НА КОЛОНИ И МНОЖЕСТВО РЕДОВЕ В НАБОРА ДАННИ

Предходният раздел ви показа как да пренастроите набор от данни, така че всеки


ред да съдържа еднакъв брой колони и да представлява единичен запис на данни.
Примерният код в този раздел илюстрира как да пренастроите колони с данни, които
са правилни по отношение на тяхното съдържание, и също така поставя два записа
във всеки ред на новия набор от данни. Листинг 7.31 показва съдържанието на mixed-
data2.csv с неправилно подравнени стойности на данните, последван от листинг 7.32,
който показва съдържанието на aligned-data2.csv с правилно форматирания набор от
данни.
Machine Translated by Google

ОБЯВКА 7.31: mixed-data2.csv

Сара, Джоунс, 1000, Калифорния, Сали, Смит, 2000, Илинойс,


Дейв, Джоунс, 3000, Флорида, Джон, Джоунс,
4000, Калифорния,

Дейв, Джоунс, 5000, Ню Йорк, Майк,


Джоунс, 6000, Ню Йорк, Тони, Джоунс, 7000, Вашингтон

ОБЯВКА 7.32: подравнени данни2.csv

Сара, Джоунс, 1000, Калифорния, Сали, Смит, 2000, Илинойс


Дейв, Джоунс, 3000, Флорида, Джон, Джоунс, 4000, Калифорния
Dave, Jones, 5000, NY, Mike, Jones, 6000, NY
Тони, Джоунс, 7000, Вашингтон

Листинг 7.33 показва съдържанието на mixed-data2.sh , което илюстрира как

за да пренастроите набора от данни в листинг 7.31.

ЛИСТИНГ 7.33: mixed-data2.sh

#---------------------------------------- # 1) премахнете празните редове # 2 ) премахнете


подаването на редове # 3) отпечатайте
LF след всеки 8 полета # 4) премахнете
крайния ',' от всеки ред #------------------------ -----------------
inputfile="mixed-data2.txt" outputfile="aligned-data2.txt"

grep -v "^$" $inputfile |awk -F"," '{printf("%s",$0)}' | awk


НАЧАЛО { columnCount = 4; брой редове = 2; currRow = 0 } {

for(i=1; i<=NF; i++) { printf("%s ", $i) if( i %


columnCount == 0) { ++currRow }
if(currRow > 0 && currRow % rowCount == 0 ) {currRow = 0; печат

“”}
}
}' > временни колони

# 4) премахване на крайния ',' от изхода: cat temp-columns | sed


's/, $//' | sed 's/ $//' > $outputfile

Списък 7.33 е много подобен на листинг 7.30. Основната идея е да се отпечата символ за подаване на

ред, след като двойка „нормални“ записи са били обработени, което е имплементирано чрез кода, показан с

удебелен шрифт в листинг 7.33.


Machine Translated by Google

Сега можете да обобщите листинг 7.33 много лесно, като промените първоначалната
стойност на променливата rowCount на всяко друго положително цяло число и кодът ще
работи правилно без допълнителни модификации. Например, ако инициализирате rowCount
на стойност 5, тогава всеки ред в новия набор от данни (с възможното изключение на
крайния изходен ред) ще съдържа 5 „нормални“ записа с данни.

ПРЕМАХВАНЕ НА КОЛОНА ОТ ТЕКСТОВ ФАЙЛ

Листинг 7.34 показва съдържанието на VariableColumns.txt с редове текст, които


съдържат различен брой колони.

ЛИСТИНГ 7.34: VariableColumns.txt

това е първи ред


това е ред номер едно, това е
третият и последен ред

Списък 7.35 показва съдържанието на RemoveColumn.sh , което премахва


първата колона от текстов файл.

ОБЯВКА 7.35: RemoveColumn.sh

awk '{ for (i=2; i<=NF; i++) printf "%s ", $i; printf "\n"; }' products.txt

Цикълът е между 2 и NF, който итерира всички полета с изключение на първото поле.
В допълнение, printf() изрично добавя нови редове. Резултатът от предходния кодов
фрагмент е тук:

400 нови
300 нови
300 използвани
200 използвани
100 използвани

ПОДМНОЖЕСТВА ОТ ПОДРАВНЕНИ ПО КОЛОНИ РЕДОВЕ В НАБОРИ ДАННИ

Списък 7.35 ви показа как да подравните редовете на набор от данни, а примерният


код в този раздел илюстрира как да извлечете подмножество от съществуващия
Machine Translated by Google

колони и подмножество от редове. Списък 7.36 показва съдържанието на sub-rows-


cols.txt на желания набор от данни, който съдържа две колони от всеки четен ред на файла
aligned-data.txt.

ОБЯВКА 7.36: sub-rows-cols.txt

Сара, 1000
Дейв, 3000
Дейв, 5000
Тони, 7000

Списък 7.37 показва съдържанието на sub-rows-cols.sh , което илюстрира как да


генерирате набора от данни в листинг 7.36. По-голямата част от кода е същият като листинг
7.33, като новият код е показан с удебелен шрифт.

ЛИСТИНГ 7.37: sub-rows-cols.sh

#---------------------------------------- # 1) премахнете празните редове # 2 )


премахване на прехвърляне на
ред(ове) # 3) отпечатване на LF след
всяко четвърто поле # 4) премахване на крайния ',' от всеки ред
#--------------------- -------------------

inputfile="mixed-data.txt"

grep -v "^$" $inputfile |awk -F"," '{printf("%s",$0)}' | awk


НАЧАЛО { columnCount = 4 } {

for(i=1; i<=NF; i++) { printf("%s ", $i)


if( i % columnCount == 0)
{ print "" }
}
}' > временни колони

# 4) премахване на крайния ',' от изхода: cat temp-columns |


sed 's/, $//' | sed 's/$//' > temp-columns2

cat temp-columns2 | awk


НАЧАЛО { rowCount = 2; currRow = 0} {

if(currRow % rowCount == 0) { print $1, $3 }


++currRow
}' > временни колони3

cat temp-columns3 | sed 's/,$//' | sed 's/ $//' > $outputfile


Machine Translated by Google

Списък 7.37 съдържа нов блок код, който пренасочва изхода от Стъпка #4 към
временен файл temp-columns2, чието съдържание се обработва от друга awk команда
в последния раздел на Списък 7.37. Забележете, че командата awk съдържа блок
BEGIN , който инициализира променливите rowCount и currRow със стойностите
съответно 2 и 0.
Основният блок отпечатва колони 1 и 3 от текущия ред, ако номерът на текущия
ред е четен, след което стойността на currRow се увеличава. Резултатът от тази команда
awk се пренасочва към още един временен файл, който е вход за крайния кодов
фрагмент, който използва командата cat и две повторения на командата sed за
премахване на завършващ „,“ и завършващ интервал, както е показано тук :

cat temp-columns3 | sed 's/,$//' | sed 's/ $//' > $outputfile

Има и други начини за изпълнение на функционалността в листинг 7.37, но


основната цел е да ви покаже различни техники за комбиниране на различни bash
команди.

БРОЕНЕ НА ЧЕСТОТАТА НА ДУМАТА В НАБОРА С ДАННИ

Листинг 7.38 показва съдържанието на WordCounts1.sh , което илюстрира как да


преброите честотата на думите във файл.

ОБЯВКА 7.38: WordCounts1.sh

awk
# Отпечатайте списък с честоти на думите {

за (i = 1; i <= NF; i++) freq[$i]++

}
КРАЙ
{ за (дума в честота) printf "%s\t%d\n",
дума, честота[дума]

} ' columns2.txt

Листинг 7.38 съдържа блок от код, който обработва редовете в columns2.txt. Всеки
път, когато се срещне дума (от ред), кодът увеличава броя на срещанията на тази дума
в хеш таблицата freq.
Machine Translated by Google

Блокът END съдържа for цикъл, който показва броя на срещанията на всяка дума в
columns2.txt.
Резултатът от листинг 7.38 е тук:

две
един
3 3 три 3
шест 1
четири 3
пет 2

Списък 7.39 показва съдържанието на WordCounts2.sh , който извършва преброяване на думи без
значение от главни и малки букви.

ОБЯВКА 7.39: WordCounts2.sh

awk
{
# преобразувайте всичко в малки букви $0 = tolower($0) #
премахнете пунктуацията
#gsub(/[^[:alnum:]_[:blank:]]/, "", $0)

for(i=1; i<=NF; i++) { freq[$i]++

}
}
КРАЙ
{ for(word in freq) {
printf "%s\t%d\n", дума, честота[дума]
}

} ' columns4.txt

Списък 7.39 започва с кодов фрагмент, който преобразува текста във всеки входен
ред в малки букви, както е показано тук:

$0 = по-ниско ($0)

Следващият кодов фрагмент премахва препинателните знаци и последващият цикъл


отчита броя на появяванията на всяка работа.
Последната част от листинг 7.39 показва всяка дума и нейната честота в текстовия
файл.
Листинг 7.40 показва съдържанието на columns4.txt.
Machine Translated by Google

ОБЯВКА 7.40: columns4.txt

123 ЕДНО ДВЕ


456 три четири
ЕДНО ДВЕ ТРИ ЧЕТИРИ
пет 123 шест
едно две три
четири пет

Резултатът от стартирането на листинг 7.39 с columns4.txt е тук:

456 1
две 3
един 3
три 3
шест 1
123 2
четири 3
пет 2

ПОКАЗВАНЕ САМО НА „ЧИСТИ“ ДУМИ В НАБОР ДАННИ

За простота, нека работим с текстов низ и по този начин можем да видим


междинни резултати, докато работим към решението. Този пример ще бъде
познат от предишни глави, но сега виждаме как го прави awk .
Листинг 7.41 показва съдържанието на onlywords.sh , който съдържа три awk
команди за показване на думите, целите числа и буквено-цифровите низове в a
текстов низ.

ОБЯВКА 7.41: onlywords.sh

x="ghi abc Ghi 123 #def5 123z"

echo "Само думи:"


'
echo $x |tr -s ' '\n' | awk -F" " '
{
if($0 ~ /^[a-zA-Z]+$/) { print $0 }
}
'
| сортиране | уникален
ехо

echo "Само цели числа:"


'
ехо $x |tr -s { ' '\n' | awk -F" " '

if($0 ~ /^[0-9]+$/) { print $0 }


Machine Translated by Google

}
'
| сортиране | уникално
ехо

echo "Само буквено-цифрови думи:" echo $x |tr -s '


'
'\n' | awk -F" " ' {

if($0 ~ /^[0-9a-zA-Z]+$/) { print $0 }


}
'
| сортиране | уникално
ехо

Листинг 7.41 започва с инициализиране на променливата x:

x="ghi abc Ghi 123 #def5 123z"

Следващата стъпка е да разделите x на думи:

'
ехо $x |tr -s ' '\н'

Резултатът е тук:

ghi
abc
Ghi
123
#def5
123z

Третата стъпка е да извикате awk и да проверите за думи, които съответстват на регулярния


израз ^[a-zA-Z]+, който съответства на всеки низ, състоящ се от една или повече главни или малки
букви (и нищо друго):

if($0 ~ /^[a-zA-Z]+$/) { print $0 }

Резултатът е тук:

ghi
abc
Ghi

И накрая, ако искате също да сортирате изхода и да отпечатате само уникалните думи,
пренасочете изхода от командата awk към командата за сортиране и командата uniq .
Machine Translated by Google

Втората awk команда използва регулярния израз ^[0-9]+ за проверка за цели числа,
а третата awk команда използва регулярния израз ^[0-9a-zA-Z]+ за проверка за буквено-
цифрови думи. Резултатът от стартирането на листинг 7.37 е тук:

Само думи:
Ghi
abc

ghi

Само цели числа: 123

Само буквено-цифрови думи: 123 123z

Ghi
абв

ghi

Сега можете да замените променливата x с набор от данни само за извличане


азбучни низове от този набор от данни.

РАБОТА С МНОГОРЕДОВИ ЗАПИСИ В AWK

Списък 7.42 показва съдържанието на employee.txt, а листинг 7.43 показва


съдържанието на Employees.sh , което илюстрира как да свържете текстови редове във
файл.

ОБЯВКА 7.42: служители.txt

Име: Джейн Едуардс

EmpId: 12345
Адрес: 123 Main Street Чикаго Илинойс

Име: Джон Смит

EmpID: 23456
Адрес: 432 Lombard Avenue SF Калифорния

ОБЯВА 7.43: служители.sh

inputfile="employees.txt" outputfile="employees2.txt"
Machine Translated by Google

awk
{
if($0 ~ /^Име:/) {
x = substr($0,8) ","
следващия

if( $0 ~ /^Empid:/) {
#пропускане на реда с данни на
Empid #x = x substr($0,7) ","
следващия

if($0 ~ /^Address:/) { x = x
substr($0,9) print x

}
}
'
< $inputfile > $outputfile

Резултатът от стартирането на кода в листинг 7.43 е тук:

Джейн Едуардс, 123 главна улица Чикаго Илинойс


Джон Смит, 432 Lombard Avenue SF Калифорния

Сега, след като сте видели изобилие от awk кодови фрагменти и shell скриптове, съдържащи
командата awk , които илюстрират различни типове задачи, които можете да изпълнявате върху
файлове и набори от данни, вие сте готови за някои случаи на употреба. Следващият раздел
(който е първият случай на употреба) ви показва как да замените няколко разделителя на
полета с един разделител, а вторият случай на употреба ви показва как да манипулирате низове
с дата.

ПРОСТ СЛУЧАЙ НА УПОТРЕБА

Примерният код в този раздел ви показва как да използвате командата awk , за да разделите
полетата, разделени със запетая, в редовете на набор от данни, където полетата могат да
съдържат вложени кавички с произволна дълбочина.
Листинг 7.44 показва съдържанието на файла quotes3.csv , който съдържа разделител „,“ и
множество полета в кавички.

ОБЯВКА 7.44: quotes3.csv

field5,field4,field3,"field2,foo,bar",field1,field6,field7,"fieldZ"
Machine Translated by Google

fname1,"fname2,други,неща",fname3,"fname4,foo,bar",fname5
"lname1,a,b","lname2,c,d","lname3,e,f","lname4,foo, бар", име5

Листинг 7.45 показва съдържанието на файла delim1.sh , който илюстрира как


за да замените разделителите в quotes3.csv със знак „,“.

ОБЯВА 7.45 delim1.sh

#inputfile="quotes1.csv"
#inputfile="quotes2.csv"
inputfile="quotes3.csv" grep -v "^$"
$inputfile | awk ' {

print "LINE #" NR ": " $0 printf


("-------------------------\n") за (i = 0; ++ i <= NF;)

printf "поле #%d : %s\n", i, $i printf ("\n")

}' FPAT='([^,]+)|("[^"]+")' < $inputfile

Резултатът от стартирането на shell скрипта в листинг 7.44 е тук:

РЕД #1:
поле5,поле4,поле3,"поле2,foo,лента",поле1,поле6,поле7,"полеZ"
------------------------
поле #1 : поле5 поле #2 :
поле4 поле #3 : поле3 поле
#4 : "поле2,foo,bar" поле
#5 : поле1 поле #6 : поле6 поле #7 : поле7
поле #8 : "полеZ"

РЕД #2: fname1,"fname2,other,stuff",fname3,"fname4,foo,bar",fname5


------------------------
поле #1 : fname1 поле #2 :
"fname2,other,stuff" поле #3 : fname3 поле #4 :
"fname4,foo,bar" поле #5 :
fname5

РЕД #3:
"lname1,a,b","lname2,c,d","lname3,e,f","lname4,foo,bar",lname5
------------------------

поле #1 : "lname1,a,b" поле #2 :


"lname2,c,d" поле #3 : "lname3,e,f"
Machine Translated by Google

поле #4 : "lname4,foo,bar" поле #5 : lname5

РЕД #4: "Външен1 "Вътрешен "Вътрешен "Вътрешен C" B" A"


Външен1","XYZ1,c,d","XYZ2lname3,e,f"
------------------------

поле #1 : "Външно1 "Вътрешно "Вътрешно "Вътрешно C" B" A" Външно1" поле #2 : "XYZ1,c,d"
поле #3 : "XYZ2lname3,e,f"

РЕД #5:
------------------------

Както можете да видите, задачата в този раздел се решава лесно чрез командата awk .

ДРУГ СЛУЧАЙ НА УПОТРЕБА

Примерният код в този раздел ви показва как да използвате командата awk , за да


преформатирате полето за дата в набор от данни и да промените реда на полетата в новия набор
от данни. Например, при следния входен ред в оригиналния набор от данни:

Джейн, Смит, 20140805234658

Преформатираният ред в изходния набор от данни има следния формат:

2014-08-05 23:46:58, Джейн, Смит

Листинг 7.46 показва съдържанието на файла dates2.csv , който съдържа „,“


разделител и три полета.

ОБЯВА 7.46 dates2.csv

Джейн, Смит, 20140805234658


Джак, Джоунс, 20170805234652
Дейв, Стоун, 20160805234655
Джон, Смит, 20130805234646
Джийн, Дейвис, 20140805234649
Тад, Смит, 20150805234637
Джак, Пруит, 20160805234638

Списък 7.47 показва съдържанието на string2date2.sh , което преобразува


полето за дата в нов формат и премества новата дата в първото поле.
Machine Translated by Google

ОБЯВКА 7.47: string2date2.sh

inputfile="dates2.csv"
outputfile="formatteddates2.csv"

rm -f $изходен файл; докоснете $outputfile

за ред в `cat $inputfile` направете fname=`echo $line |


cut

-d"," -f1` lname=`echo $line |cut -d"," -f2` date1=`echo $line |cut -
d"," -f3`

# конвертиране в нов формат на дата


newdate='echo $date1 | awk '{ print substr($0,1,4)"-
"substr($0,5,2)"-"substr($0,7,2)"
"substr($0,9,2)":"substr($0,11,2)":"substr($0,13, 2)}''

# добавяне на новоформатиран ред към изходния файл echo "${newdate},${fname},


${lname}" >> $outputfile done

Съдържанието на новия набор от данни е тук:

2014-08-05 23:46:58, Джейн, Смит


2017-08-05 23:46:52, Джак, Джоунс
2016-08-05 23:46:55, Дейв, Стоун
2013-08-05 23:46:46, Джон, Смит
2014-08-05 23:46:49, Жан, Дейвис
2015-08-05 23:46:37, Тад, Смит
2016-08-05 23:46:38, Джак, Пруит

РЕЗЮМЕ

Тази глава представи командата awk , която по същество е цяла


език за програмиране, пакетиран в една Unix команда.
Изследвахме някои от вградените му променливи, както и условната логика, циклите while и for циклите в

awk за манипулиране на редовете и колоните в наборите от данни. След това видяхте как да изтривате редове и

да обединявате редове в набори от данни, както и как да отпечатвате съдържанието на файл като един ред текст.

След това научихте как да използвате метасимволи и набори от знаци в awk команди. Научихте как да извършвате
числени изчисления (като добавяне, изваждане, умножение и деление) във файлове, съдържащи числови данни,

както и някои цифрови функции, които са налични в awk.


Machine Translated by Google

Освен това научихте как да подравнявате, изтривате и избирате подмножество от


колони и да работите с многоредови записи в набори от данни. И накрая, видяхте
няколко прости случая на употреба, включващи вложени кавички и формати на дата в
структуриран набор от данни.
На този етап разполагате с всички инструменти, необходими за извършване на
сложно почистване и обработка на данни и е силно препоръчително да се опитате да ги
приложите към задача или проблем, които представляват интерес. Последната стъпка
от учебния процес е работата по проект от реалния свят.
Може да си помислите „Веднъж видях нещо подобно, чудя се дали има начин да...“
или дори „как да направя XXX на език YYY?“ Тази книга ви дава представа какво е
възможно.
В този момент има още нещо да кажем: поздравления! Завършили сте бърза, но
плътна книга и ако сте баш неофит, материалът вероятно ще ви занимава много часове.
Примерите в главите осигуряват солидна основа, а Приложението съдържа допълнителни
примери и случаи на употреба, за да илюстрира допълнително как Unix командите
работят заедно. Комбинираният ефект показва, че вселената от възможности е по-голяма
от примерите в тази книга и в крайна сметка те ще предизвикат идеи във вас. Късмет!
Machine Translated by Google

ГЛАВА 8

ВЪВЕДЕНИЕ В SHELL СКРИПТОВЕ


И ФУНКЦИИ

неговата глава ви запознава със скриптове на shell, които илюстрират как да решавате
T grep команда,
някои така чезадачи. Някои примери разчитат на това
добре познати
би било подходящо време да прегледате материала в главата, която съдържа
информация, свързана с grep. По-късно в тази глава ще видите шел скриптове, които
включват базирани на рекурсия алгоритми за добре познати задачи като най-големия
общ делител (НОД) и най-малкото общо кратно (НКМ) на две положителни цели числа.

Първата част на тази глава започва с примери за прости шел скриптове и как да
направите тези шел скриптове изпълними. Този раздел също така ви показва как да
„извлечете“ или „точка“ скрипт на обвивка и описва ситуации, когато е необходимо да се
направи това.
Втората част на тази глава ви показва как да използвате параметри за предаване на
функции на обвивка, които са дефинирани в скриптове на обвивка, да определите броя на
стойностите, предадени на функция, и да покажете техните стойности. Този раздел също
така съдържа пример за интерактивен shell скрипт (т.е. подканва потребителите да
въвеждат).
Третата част на тази глава ви показва как да използвате рекурсия, за да изчислите
стойността на факториела на положително цяло число. В допълнение, този раздел ви
показва скриптове на обвивката за изчисляване на числата на Фибоначи, GCD и LCM на
две положителни цели числа и делителите на положително цяло число.
Machine Translated by Google

Въпреки че някои от примерите може да нямат незабавна стойност за вас, все пак си струва да
отделите време да ги прочетете, за да видите дали съдържат техники, които можете да използвате във
вашите собствени скриптове на обвивката.

КАКВО ПРЕДСТАВЛЯВАТ SHELL СКРИПТОВЕ?

Shell скриптовете съдържат колекция от bash команди, които се изпълняват за изпълнение на


задача, дефинирана от вас. Ако shell скриптът не съдържа никакви функции, тогава командите се
изпълняват последователно отгоре надолу (т.е. в последователността, в която се появяват в shell
скрипта). Както ще видите по-късно в тази глава, можете да дефинирате функции и да използвате
условна логика, за да промените последователността на изпълнение на командите.

Скриптовете на Shell могат да съдържат всички команди bash, налични във вашата система, но
някои команди изискват командата sudo , която от своя страна изисква парола. Простите примери за
скриптове на обвивката включват команди, свързани с файлове, които създават файлове, четат данни
от файлове и актуализират съдържанието на файловете. Независимо от съдържанието на вашите шел
скриптове, те се интерпретират „в движение“, така че няма стъпки за компилиране, които създават
двоичен изпълним файл.
Shell скриптовете автоматизират процеса на изпълнение на набор от bash команди, така че да не
е необходимо да ги изпълнявате ръчно от командния ред. Ако трябва да изпълните проста команда от
командния ред, тогава е малко вероятно да трябва да го направите чрез шел скрипт: просто въведете
командата и натиснете клавиша <RETURN> . Обърнете внимание, че помощната програма bash crontab
ви позволява да планирате изпълнението на shell скриптове на регулярна основа (почасово, ежедневно,
седмично и т.н.). Глава 10 предоставя допълнителна информация относно помощната програма
crontab .

Прост шел скрипт Този раздел

ви показва как да създадете шел скрипт, който съдържа набор от прости команди, които се
изпълняват последователно. По-конкретно, създайте текстовия файл test.sh (използвайки любимия си
текстов редактор) със следното съдържание:

#!/bin/bash pwd ls
cd /
tmp
Machine Translated by Google

ls

mkdir /tmp/abc докосване /


tmp/abc/emptyfile ls /tmp/abc/

Втората стъпка включва превръщането на този shell скрипт в изпълним, което


включва командата chmod , както е показано тук:

chmod +x test.sh

Сега вашият shell скрипт е готов за изпълнение. Въведете следното


команда в директорията, която съдържа test.sh:

./test.sh

ЗАБЕЛЕЖКА Резултатът от стартирането на test.sh зависи от съдържанието на директорията /tmp .

Първият ред в test.sh се нарича ред "shebang", който насочва системата да стартира
bash shell, за да извика командите в test.sh. Терминът "shebang" е нещо като смесване
на думите "hash" (за знака "#") и "bang" (за знака "!"). Обърнете внимание, че началният
“./” на ./test.sh указва файла test.sh в текущата директория: ако файлът test.sh е във
вашата домашна директория, посочете $HOME/test.sh. Освен това, ако „.“ е включена в
променливата на средата PATH , тогава можете просто да въведете test.sh без префикса
“./” .

Една точка относно командата mkdir : ако посочите път, в който не съществуват
междинни директории, тогава трябва да използвате превключвателя. Например,
-стр ако
директорията /tmp/abc не съществува, следната команда изисква
-стр превключвател:

mkdir -p /tmp/abc/def

ЗАДАВАНЕ НА ПРОМЕНЛИВИТЕ НА СРЕДАТА ЧРЕЗ SHELL СКРИПТОВЕ

Важна концепция при използване на шел скриптове е, че всички променливи,


зададени вътре в скрипта, вече не са зададени, когато скриптът завърши изпълнението.
Правилата са както следва:
Machine Translated by Google

• Ако дадена променлива не е зададена в скрипт, но вече е дефинирана преди


изпълнението на скрипта, тази променлива също ще бъде налична в скрипта. •
Ако променлива е зададена в скрипт, тя ще замени всяка съществуваща променлива
със същото име, след като променливата е зададена, но след като скриптът
приключи, променливата ще се върне към старата си стойност (или без стойност,
ако не е съществуват извън скрипта на обвивката).

Например, ако вашата $HOME директория е /Users/jsmith , но вътре в скрипт на ред


10 дефинирате $HOME да бъде /Users/common/bin, тогава стойността на $HOME
първоначално е /Users/jsmith за редове 1-9 , след това става /Users/common/bin на ред 10
и поддържа тази стойност, докато не бъде изпълнена последната команда в скрипта на
обвивката. След това стойността се връща към /Users/jsmith.
Причината за това поведение е свързана с начина, по който Unix структурира своите
процеси (известни като „обвивки“, оттук и терминът „скрипт на обвивката“). Тази дискусия
е извън обхвата на тази книга, но можете да извършите онлайн търсене, за да намерите
статии с подробно обяснение.
По този начин поведението по подразбиране е, че ако зададете стойността на
променлива в shell скрипт, тогава тази променлива (и нейната стойност) съществува само
по време на изпълнението на shell скрипта. Има просто „заобиколно решение“, при което
променливите „задържат“ стойностите си след завършване на скрипта на обвивката и ще
научите как да го направите в следващ раздел.
Само за да се уверите, че разликата е ясна, разгледайте листинг 8.1, който показва
съдържанието на скрипта на обвивката abc.sh.

ОБЯВА 8.1: abc.sh

експортиране x="123" ехо


"вътре в abc.sh" ехо "x = $x"

Уверете се, че abc.sh е изпълним shell скрипт с командата chmod (както е показано
по-рано в тази глава) и след това стартирайте следната последователност от команди от
командния ред:

експортиране x="tom"
echo "x = $x" ./abc.sh
echo "x = $x"

Резултатът от предходните команди е тук:


Machine Translated by Google

x = том
вътре в abc.sh x = 123

x = том

Както можете да видите, стойността, която е присвоена на променливата x е само за


продължителността на процеса, свързан със скрипта на обвивката abc.sh. След като
изпълнението приключи, процесът прекратява и стойността на x се връща към
първоначалната си стойност. За щастие, има начин да се гарантира, че стойностите на
променливите в скрипта на обвивката могат да бъдат „настроени“ за текущата обвивка,
техника, наречена „извличане“ на скрипта на обвивката, както е описано в следващия раздел.

Снабдяване или „начертаване“ на Shell Script

Изпълнете следната последователност от команди:

експортиране x="tom smith" echo "x


= $x" . abc.sh

ехо "x = $x"

Резултатът от предходните команди е тук:

x = "том смит"
вътре abc.sh
x = 123 x
= 123

В предходния кодов блок стойността, присвоена на променливата x вътре в скрипта


на обвивката abc.sh , замества нейната предварително дефинирана стойност, тъй като
„извеждането“ (наричано още „точкуване“) на скрипт на обвивката не създава нов процес.
Следователно, ако шел скрипт присвои нова стойност на съществуваща променлива,
тази нова стойност се поставя в текущата среда и предварително дефинираната стойност
се губи.
Както вероятно знаете, коментарите са важни в изходния код. Добрият shell скрипт
съдържа смислени коментари, които са предшествани от знак „#“, които обясняват
предназначението на различните секции в shell скрипта. Изключение е, когато символът
"#" се появява в първия ред на скрипт на обвивката, както ще видите в следващия раздел.
Machine Translated by Google

РАБОТА С ФУНКЦИИ В SHELL СКРИПТОВЕ

Функция на обвивката може да бъде дефинирана чрез използване на ключовата дума


функция, последвана от името на функцията (посочено от вас) и двойка кръгли скоби,
последвани от двойка фигурни скоби, които съдържат команди на обвивката.
Общият формуляр е показан тук:

функция fname() {

изявления;
}

Алтернативен метод за дефиниране на функция на обвивка включва поставяне на лявата


къдрава скоба на отделен ред, както е показано тук:

fname() {

изявления;
}

Функция на обвивката може да бъде извикана от нейното име, както е показано тук:

fname ; # изпълнява функцията

Аргументите могат да се предават на функции и могат да бъдат достъпни от скрипта на


обвивката:

fname arg1 arg2; # предаване на аргументи

Листинг 8.2 показва съдържанието на simple-shell.sh , което илюстрира как да


дефинирайте функция в шел скрипт.

ЛИСТИНГ 8.2: simple-shell.sh

#!/bin/sh

функция1 () {

ехо "вътре във функция 1"


}

функция2 () {

echo "Въведохте $1 във функция 2"


Machine Translated by Google

# извикване на функция1 тук: функция1

echo "Въведете низ: " прочетете str

# извикване на функция2 тук: функция2 str

Листинг 8.2 дефинира функция1, която показва текстово съобщение, и


функция2, която показва низа, който сте въвели в командния ред.
Стартирайте листинг 8.2 и въведете abc в подканата и ще видите следния резултат:

вътрешна функция 1

Въведете низ: abc

сте въвели str във функция 2

ПРЕДАВАНЕ НА СТОЙНОСТИ КЪМ ФУНКЦИИ В SHELL СКРИПТ (1)

Позиционните параметри са вградени променливи, които съдържат стойностите


на аргументите на командния ред към скриптове и функции в скриптове на
обвивката. Позиционните параметри се наричат 1, 2, 3 и т.н., а стойностите им се
означават с $1, $2, $3 и т.н. Позиционният параметър 0 има стойност, равна на
името на скрипта.
Листинг 8.3 показва съдържанието на parameters-function.sh , което илюстрира
как да подадете стойности към функция в скрипт на обвивка.

ЛИСТИНГ 8.3: parameters-function.sh

#!/bin/sh

функция1 () {

echo "отгоре на функция 1" echo "param 1: $1"


echo "param 2: $2" echo "param 3:
$3"

# извикване на функция1 тук:


Machine Translated by Google

функция1 а
функция1 аб
функция1 abc

Листинг 8.3 дефинира function1(), която показва стойностите на първите три


параметъра, които получава. Стартирайте Листинг 8.3 и ще видите следния резултат:

в началото на функция 1
параметър 1: a
параметър
2: параметър
3: в началото на функция
1 параметър 1:
a параметър 2:
b параметър
3: в началото на функция
1 параметър 1:
a параметър 2:
b параметър 3: c

Има очевиден проблем с функцията в листинг 8.3: тази функция предполага, че


има точно три параметъра. Следователно той показва параметри, които нямат
стойности, и не показва стойностите извън първите три параметъра.

Решението включва определяне на броя на параметрите, които се предоставят


на функция, което е темата на следващия раздел.

ПРЕДАВАНЕ НА СТОЙНОСТИ КЪМ ФУНКЦИИ В SHELL СКРИПТ (2)

Две специални променливи съдържат всички позиционни параметри (с


изключение на позиционния параметър 0): * и @. Разликата между тях е фина, но
важна и е очевидна само когато са в двойни кавички.

Списък 8.4 показва съдържанието на parameters-function2.sh , което илюстрира


как да се определи броя на стойностите, които се предават на функция в скрипт на
обвивка.

ЛИСТИНГ 8.4: parameters-function2.sh

#!/bin/sh

функция1 ()
Machine Translated by Google

{
echo "брой параметри: $#" echo "всички
параметри: $@" echo ""

# извикване на функция1 тук: функция1 a


функция1 ab

функция1 abc
функция1 abcd функция1 1 2 3 4
5

# показва стойностите на командния ред: echo "параметри: $#"


echo "всички параметри: $@"

Както можете да видите, функцията в листинг 8.4 използва $3 за показване на броя


на параметрите и $@ за показване на техните стойности при всяко извикване на
функцията. Стартирайте листинг 8.4 и ще видите следния резултат:

брой параметри: 1 всички


параметри: a

брой параметри: 2 всички


параметри: ab

брой параметри: 3 всички


параметри: abc

брой параметри: 4 всички


параметри: abcd

брой параметри: 5 всички


параметри: 1 2 3 4 5 параметър 3: c

брой параметри: 2 всички


параметри: добра паста

ПОВТОРЕНИЕ НА СТОЙНОСТИ, ПРЕДАДЕНИ КЪМ ФУНКЦИЯ

Като друг пример, тази функция на обвивката и извикване на обвивката


функция ви показва как да изброите стойностите на параметрите в удобен блок:

show_args ()
Machine Translated by Google

{
echo "Брой аргументи: $#" echo "Име на
скрипт: $0" echo "Първи аргумент: $1" echo
"Втори аргумент: $2" echo "Трети аргумент:
$3" echo "Всички аргументи: $@"

show_args пица Ню Йорк Чикаго

Резултатът от предишния кодов блок е тук:

Брой аргументи: 4 Име на


скрипта: ./arguments.sh Първи аргумент: нов Втори
аргумент: york Трети аргумент:
chicago Всички аргументи: new york
chicago pizza

Листинг 8.5 показва съдържанието на iterate-args1.sh , което илюстрира как


за итерация през набор от стойности в шел скрипт.

ЛИСТИНГ 8.5: iterate-args1.sh

#!/bin/sh

for i in {1..5} do echo

"Стойност на i: ${i}" done

Както можете да видите, функцията в листинг 8.5 итерира стойностите от 1 до


5 включително. Стартирайте листинг 8.5 и ще видите следния резултат:

Стойност на i: 1
Стойност на i: 2
Стойност на i: 3
Стойност на i: 4
Стойност на i: 5

Листинг 8.6 показва съдържанието на iterate-args2.sh , което илюстрира как


за итерация през набор от стойности в шел скрипт.

ЛИСТИНГ 8.6: iterate-args2.sh


Machine Translated by Google

#!/bin/sh

iterate() {

за arg

направете echo "стойност: $arg";


Свършен
}

итерация abcde

Както можете да видите, функцията в листинг 8.6 итерира стойностите, които


се предават на функцията iterate(). Стартирайте Листинг 8.6 и ще видите следния
резултат:

стойност: а
стойност: b
стойност: c
стойност: d
стойност: e

Листинг 8.7 показва съдържанието на iterate-args3.sh , което илюстрира как


за итерация през набор от стойности в шел скрипт.

ЛИСТИНГ 8.7: iterate-args3.sh

#!/bin/sh

iterate() {

echo "това ще бъде пропуснато … защо?"


}

iterate() {

arg1="$1"; смяна;

за arg

направете echo "стойност: $arg";


Свършен
}

итерация abcde
Machine Translated by Google

Както можете да видите, функцията в листинг 8.7 итерира стойностите, които се предават на
функцията iterate(). Има обаче две дефиниции на функцията iterate() и правилото е просто:
последната (най-долната) дефиниция се изпълнява и всички други дефиниции се игнорират
(може да има повече от две дефиниции на една и съща функция).

Друга подробност, която трябва да забележите, е първият кодов фрагмент, който е показан с удебелен шрифт

(втората) дефиниция на функцията iterate() и възпроизведена тук:

arg1="$1"; смяна;

Предходният кодов фрагмент запазва стойността на $1 в променливата arg1, само в случай,


че искате да обработите тази стойност на друго място в кода (което просто игнорираме в този
примерен код). След това ключовата дума shift извършва „преместване наляво“ на набор от
аргументи, които са били предадени на функцията iterate. В резултат на това $1 се заменя с $2, а
$2 се заменя с $3 и така нататък, докато всички аргументи се изместят наляво. Стартирайте
листинг 8.7 и ще видите следния резултат:

стойност: b
стойност: c
стойност: d
стойност: e

Листинг 8.8 показва съдържанието на iterate-args3.sh , което илюстрира как


да използвате for цикъл за итерация през набор от стойности в скрипт на обвивка.

ЛИСТИНГ 8.8: iterate-args4.sh

#!/bin/sh

iterate() {

for (( i=2; i <= "$#"; i++ )) do echo "arg position: ${i}" ${!i}"

echo "arg value: done

итерация abcde

Както можете да видите, функцията в листинг 8.8 итерира стойностите, които се предават на
функцията iterate(). Цикълът for обаче започва от
Machine Translated by Google

стойност 2, която пропуска първия аргумент. Стартирайте листинг 8.8 и ще


видите следния резултат:

позиция на аргумент: 2
стойност на аргумент: b
позиция на аргумент: 3
стойност на °С

аргумент: позиция на
аргумент: 4 стойност на
аргумент: d позиция на
д
аргумент: 5 стойност на аргумент:

Листинг 8.9 показва съдържанието на iterate-args5.sh , което илюстрира как


да използвате for цикъл за итерация през набор от стойности, някои от които
са в кавички, в скрипт на обвивка.

ЛИСТИНГ 8.9: iterate-args5.sh

#!/bin/sh

iterate() {

echo "Брой аргументи: $#" echo "Списък с


аргументи: $@" echo ""

for i in "${@}" do echo

"argument: $i" done

итерация на "b c" d "e f"

Както можете да видите, функцията в листинг 8.9 итерира стойностите,


които се предават на функцията iterate(). Ако два или повече низа са вътре в
двойка кавички, те се третират като единичен аргумент. Стартирайте листинг
8.9 и ще видите следния резултат:

Брой аргументи: 4
Списък с аргументи: abcdef

аргумент: a аргумент:
bc
Machine Translated by Google

аргумент: d аргумент:
еф

ПОЗИЦИОННИ ПАРАМЕТРИ В ДЕФИНИРАНИ ОТ ПОТРЕБИТЕЛ ФУНКЦИИ

По-рано в тази глава видяхте различни примери за предаване на стойности към


функции на обвивката. Следният списък с позиционни параметри е полезен, когато
пишете функции на обвивката:

$# съдържа броя на аргументите $0 съдържа името на командата


$1, $2, … , $9 съдържа отделните аргументи на
командата $* съдържа целия списък с аргументи, третиран като една дума $@ съдържа целия списък с аргументи,
третиран като поредица от думи $? съдържа изходния статус на предишната команда, а стойността 0 означава успешно
завършване $$ съдържа идентификатора на процеса на текущия процес

Листинг 8.10 показва съдържанието на PositionalParameters1.sh , който


показва стойностите на предходните позиционни параметри.

ЛИСТИНГ 8.10: PositionalParameters1.sh

echo "брой аргументи: $#" echo "име на


командата: $0" echo "всички
параметри: $1 $2 $3 $4 $5 $6 $7 $8 $9" echo "всички параметри: $*" echo
"всички параметри: $@" echo
"изход състояние: $?" echo
"идентификатор на процес: $$"

if [ x"$1" != "x" ] then echo

"Позиционен параметър #1 = $1" else echo "Позиционен параметър


#1 е

нула" fi

if [ "$5" == then echo ""]

"Позиционният параметър #5 е nul1" fi

case $1 in n|N) echo


"#1 е n или N" ;;
Machine Translated by Google

y*|Y*) echo "#1 започва с ay или Y" ;; *) echo "няма съвпадения" ;;

esac

Стартирайте Listing 8.x със следната команда:

./PositionalParameters1.sh да 2 3 4

Резултатът е показан тук:

./PositionalParameters1.sh 2 3 4 брой аргументи: 3 име на


команда: ./PositionalParameters1.sh всички
параметри: 2 3 4 всички параметри: 2 3 4 всички параметри: 2 3 4 състояние
на изход: 0 идентификатор на
процеса: 58003 Параметър на
позиция # 1 = 2 Позиционен
параметър #5 е nul1 Няма

съвпадения

SHELL СКРИПТОВЕ, ФУНКЦИИ И ВЪВЕЖДАНЕ НА ПОТРЕБИТЕЛЯ

Листинг 8.11 показва съдържанието на checkuser.sh , което илюстрира как да


подканите потребителите за два входни низа и след това да извикате функция с тези
низове като параметри.

ОБЯВКА 8.11: checkuser.sh

#!/bin/bash

функция checkNewUser() {

echo "аргумент #1 = $1" echo "аргумент #2


= $2" echo "аргумент брой = $#"

if test "$1" = "John" && test "$2" = "Smith" then return 1 else

върнете 0
фи
}
Machine Translated by Google

"
/bin/echo -n "Собствено име: прочетете fname /
bin/echo -n
"
"Фамилно име: прочетете lname

checkNewUser $fname $lname echo "резултат = $?"

Листинг 8.11 съдържа функцията checkNewUser() , която показва стойността на


първия аргумент, втория аргумент и общия брой аргументи.
Тази функция връща стойност 1, ако първият аргумент е Джон , а вторият аргумент е
Смит; в противен случай функцията връща 0.
Останалата част от листинг 8.11 извиква командата echo два пъти, за да подкани
потребителите да въведат собствено и фамилно име и след това извиква функцията
checkNewUser () с тези две входни стойности. Примерен резултат от стартирането на
листинг 8.11 е показан тук:

Първо име: Джон


Фамилия: Смит аргумент #1 =

Джон аргумент #2 = Смит брой


аргументи = 2 резултат = 1

Какво ще кажете за използването на заместване на команда за извикване на


функцията checkNewUser? За да разберете какво ще се случи, нека добавим следния
кодов фрагмент в края на листинг 8.11:

резултат=`checkNewUser $fname $lname` echo "резултат = $резултат"

Стартирайте модифицираната версия на листинг 8.11, осигурете същите входни


стойности на John и Smith и сравнете следния резултат с предишния резултат:

Първо име: Джон


Фамилия: Смит аргумент #1 =

Джон аргумент #2 = Смит брой


аргументи = 2 резултат = 1 резултат
= аргумент #1 = Джон
аргумент #2 =

Смит аргумент брой = 2


Machine Translated by Google

Като друг пример за прост скрипт на обвивката, следващият скрипт използва


командата read , която приема входа от клавиатурата и присвоява тази входна
стойност като стойност на променливата PERSON. Командата echo отпечатва
входната стойност на STDOUT, който е екранът (по подразбиране).

#!/bin/sh echo
"Как се казваш?" read PERSON echo "Здравей,
$PERSON"

Ето примерно извикване на този скрипт:

$./test.sh Как се
казвате?
Джон Смит

Здравей, Джон Смит

РЕКУРСИЯ И SHELL СКРИПТОВЕ

Този раздел съдържа няколко примера за shell скриптове с рекурсия, което е


тема, която се среща в много езици за програмиране. Въпреки че вероятно няма
да е необходимо да пишете много скриптове, които използват рекурсия, струва
си да научите тази концепция, особено ако планирате да изучавате други езици.

Ако вече разбирате рекурсия, тогава скриптовете в този раздел ще бъдат


ясни. По-специално, ще научите как да изчислявате факториела на положително
цяло число.
Листинг 8.12 показва съдържанието на Factorial.sh , което изчислява
факторна стойност на положително цяло число.

ЛИСТИНГ 8.12: Factorial.sh

#!/bin/sh

факториал() {

if [ "$1" -gt 1 ] then decr=`expr $1


- 1`

result=`factorial $decr` product=`expr


$1 \* $result` echo $product
Machine Translated by Google

else #
стигнахме до 1: echo 1

фи
}

echo "Въведете число: "


прочетете бр

# добавете код, за да сте сигурни, че е положително цяло число

echo "$num! = `факториал $num`"

Листинг 8.12 съдържа функцията factorial() с условна логика: ако първият параметър
е по-голям от 1, тогава променливата decr се инициализира като 1 по-малко от стойността
на $1, последвано от инициализиране на резултата с рекурсивно извикване на функцията
factorial() с аргумент обв. И накрая, този блок код инициализира продукта като стойност
от $1 , умножена по стойността на резултата. Имайте предвид, че ако първият параметър
не е по-голям от 1, тогава се връща стойност 1.

Последната част от Листинг 8.12 подканва потребителите да въведат число и след


това факторната стойност на това число се изчислява и показва. За простота, стойностите,
които не са цели числа, не се проверяват (можете да опитате сами да добавите тази
функционалност).
Стартирайте кода в листинг 8.12 и ще видите следния резултат:

Въведете номер:

7 7! = 5040

ИТЕРАТИВНИ РЕШЕНИЯ ЗА ФАКТОРИАЛНИ СТОЙНОСТИ

Списък 8.13 показва съдържанието на Factorial2.sh , който изчислява факториелната


стойност на положително цяло число с помощта на for цикъл.

ЛИСТИНГ 8.13: Factorial2.sh

#!/bin/bash

факториал() {

num=$1
резултат=1
Machine Translated by Google

за (( i=2; i<=${num}; i++ )); do result=$((${result}*$i)) done

ехо $резултат
}

printf "Въведете число: " прочетете номер

echo "$num! = `факториал $num`"

Списък 8.13 съдържа функция, наречена factorial() , която инициализира


променливата num към първия аргумент, предаден на функцията factorial(), последван
от променливата result, чиято начална стойност е 1. Следващата част от листинг 8.13 е
for цикъл, който итеративно умножава стойността на резултата по числата между 2 и
num включително и след това връща стойността на променливата резултат.

Последната част от листинг 8.13 подканва потребителите за число и след това


използва заместване на команда, за да извика функцията factorial() с предоставената от
потребителя стойност. Имайте предвид, че не се извършва проверка, за да се гарантира,
че входната стойност е неотрицателно цяло число. Изявлението за ехо показва
изчислената факторна стойност.
Стартирайте кода в листинг 8.13 и ще видите следния резултат:

Въведете число: 8
8! = 40320

Списък 8.14 показва съдържанието на Factorial3.sh , който изчислява стойността на


факториела на положително цяло число, използвайки for цикъл и масив, който следи
междинните стойности на факториела.

ЛИСТИНГ 8.14: Factorial3.sh

#!/bin/bash

факториал() {

num=$1
резултат=1 за
(( i=2; i<=${num}; i++ )); направете резултат=$((${резултат}
*$i))
Machine Translated by Google

factvalues[$i]=$готов резултат

printf "Въведете число: " прочетете число

за (( i=1; i<=${num}; i++ )); do factvalues[$i]=1 готово

факториел $num

# печат на всеки елемент чрез цикъл: for (( i=1; i<=${num}; i+


+ )); do echo "Факториал на $i : готово

" ${factvalues[$i]}

Списък 8.14 е много подобен на кода в листинг 8.13. Основната разлика е,


че междинните факторни стойности се съхраняват в масива factvalues.
Забележете, че първоначалният цикъл инициализира стойностите във
factvalues: това прави стойностите глобални, така че не е необходимо да
връщаме нищо от функцията factorial() .
Последната част от листинг 8.14 съдържа for цикъл, който показва
междинните факторни стойности, както и факториела на предоставения от
потребителя вход.
Стартирайте кода в листинг 8.14 и ще видите следния резултат:

Въведете число: 9
Факториал на 1: 1
Факториал на 2: 2
Факториел от 3 : 6
Факториел от 4:24
Факториел от 5:120
Факториел от 6 : 720
Факториел от 7 : 5040
Факториел от 8 : 40320
Факториел от 9: 362880

ИЗЧИСЛЯВАНЕ НА ЧИСЛАТА НА ФИБОНАЧИ

Последователността на Фибоначи се дефинира, както следва:


Machine Translated by Google

F(1) = 1; F(2) = 2; и F(n) = F(n-1) + F(n-2) за n >= 2.

Листинг 8.15 показва съдържанието на Fibonacci.sh , което изчислява

Стойност на Фибоначи на положително цяло число.

ЛИСТИНГ 8.15: Fibonacci.sh

#!/bin/sh
LOGFILE="/tmp/a1" rm -f $LOGFILE
2>/dev/null fib() {

if [ "$1" -gt 3 ] тогава

echo "1 = $1 2 = $2 3 = $3" >> $LOGFILE

decr1='expr $2 - 1' decr2='expr $3 - 1'


decr3='expr $3 - 2'

ехо "d1 = $decr1 d2 = $decr2 d3 = $decr3" >> $LOGFILE

fib1='fib $2 $3 $decr2' fib2='fib $3 $decr2


$decr3' fib='expr $fib1 + $fib2' echo $fib else if [ "$1"
-eq 3 ] then echo 2

друго
ехо 1 фи

фи
}

echo "Въведете число: " прочетете номер

# добавете код, за да сте сигурни, че е положително цяло число

if [ "$num" -lt 3 ] then echo "fibonacci


$num
= 1" else decr1='expr $num - 1' decr2='expr $num
- 2'

echo "fibonacci $num = 'fib $num $decr1


$decr2'" fi
Machine Translated by Google

Списък 8.15 съдържа код, който намалява две променливи, наречени decr1 и
decr2, за да направи рекурсивни извиквания на функцията fib() , докато изчисляването
на факторните стойности включва намаляване само на една променлива.
Освен това листинг 8.15 съдържа кодов фрагмент, който записва междинни
изчисления в текстов файл, който след това можете да прегледате, за да проследите
пътя на изпълнение на кода. Стартирайте кода в листинг 8.15 и ще видите следния
резултат:

Въведете число: 10

фибоначи 10 = 55

ИЗЧИСЛЯВАНЕ НА НОД НА ДВЕ ЦЕЛИ ПОЛОЖИТЕЛНИ ЧИСЛА

Списък 8.16 показва съдържанието на шел скрипта gcd.sh , който изчислява


НОД на две положителни цели числа.

ОБЯВКА 8.16: gcd.sh

#!/bin/sh

функция gcd() {

if [ $1 -lt $2 ] then result=`gcd


$2 $1`

echo $result else remainder=`expr


$1 % $2`

if [ $remainder == 0 ] then echo $2 else


echo
`gcd $2

$remainder` fi

фи
}

a="4"
b="20"

result=`gcd $a $b` echo "GCD на $a


и $b = $result"
Machine Translated by Google

a="4"
b="22"

result=`gcd $a $b` echo "GCD на $b


и $a = $result"

a="20"
b="3"

result=`gcd $a $b` echo "GCD на $b


и $a = $result"

a="10"
b="10"

result=`gcd $a $b` echo "GCD на $b


и $a = $result"

Списък 8.16 е проста реализация на евклидовия алгоритъм (проверете Wikipedia


за подробности) за намиране на НОД на две положителни цели числа. Резултатът от
листинг 8.16 показва GCD на 4 и 20, както е показано тук:

НОД на 4 и 20 = 4
НОД на 22 и 4 = 2
НОД на 3 и 20 = 1
НОД на 10 и 10 = 10

ИЗЧИСЛЯВАНЕ НА LCM НА ДВЕ ЦЕЛИ ПОЛОЖИТЕЛНИ ЧИСЛА

Листинг 8.17 показва съдържанието на шел скрипта lcm.sh , който изчислява LCM
на две положителни цели числа. Този скрипт съдържа кода в скрипта на обвивката
gcd.sh за изчисляване на LCM на две положителни цели числа.

ОБЯВКА 8.17: lcm.sh

#!/bin/sh

функция gcd() {

if [ $1 -lt $2 ] then result=`gcd


$2 $1`

echo $result else remainder=`expr


$1 % $2`

if [ $remainder == 0 ]
Machine Translated by Google

then
echo $2 else

result=`gcd $2 $remainder` echo $result fi

фи
}

функция lcm() {

gcd1=`gcd $1 $2` lcm1=`expr


$1 / $gcd1` lcm2=`expr $lcm1 \* $2`
echo $lcm2

a="24"
b="10"
result=`lcm $a $b` echo "LCM
на $a и $b = $result"

a="10"
b="30"
result=`lcm $a $b` echo "LCM
на $a и $b = $result"

Забележете, че листинг 8.17 съдържа функцията gcd() за изчисляване на GCD на две


положителни цели числа. Тази функция е необходима, тъй като следващата част от листинг 8.17
съдържа функцията lcm() , която извиква функцията gcd() , последвана от някои стъпки на
умножение за изчисляване на LCM на две числа.
Резултатът от листинг 8.17 показва LCM от 10 и 24, както е показано тук:

LCM от 24 и 10 = 120
LCM от 10 и 30 = 30

ИЗЧИСЛЯВАНЕ НА ПРОСТИ ДЕЛИТЕЛИ

Списък 8.18 показва съдържанието на шел скрипта Divisors2.sh, който


изчислява простите множители на положително цяло число.

ЛИСТИНГ 8.18: Делители2.sh

#!/bin/sh
Machine Translated by Google

функция divisors() {

div="2"
num="$1"
прости числа=""

while (true) do

remainder=`expr $num % $div` if [ $remainder == 0 ] then


#echo "divisor: $div" primes="${primes}
$div"
num=`expr $num / $ div` else div=`expr
$div + 1` fi

if [ $num -eq 1 ] then break

фи
Свършен

# използвайте 'echo' вместо 'return' echo $primes

num="12"

primes=`divisors $num` echo "Простите


делители на $num: $primes"

num="768"

primes=`divisors $num` echo "Простите


делители на $num: $primes"

num="12345"

primes=`divisors $num` echo "Простите


делители на $num: $primes"

num="23768"

primes=`divisors $num` echo "Простите


делители на $num: $primes"

Списък 8.18 съдържа функцията divisors() , която се състои основно от цикъл while ,
който проверява за делителите на num (което се инициализира като стойност $1).
Първоначалната стойност на div е 2 и всеки път, когато div дели, стойността наброй,
div се
добавя към низа с прости числа и num се заменя с
Machine Translated by Google

брой/раздел. Ако div не разделя num, div се увеличава с 1. Имайте предвид, че


цикълът while в листинг 8.4 прекратява, когато num достигне стойността 1.
Резултатът от списък 8.18 показва простите делители на 12, 768,
12345 и 23768, както е показано тук:

Първите делители на 12: 2 2 3


Първите делители на 768: 2 2 2 2 2 2 2 2 3
Простите делители на 12345: 3 5 823
Простите делители на 23768: 2 2 2 2971

Простите множители на 12 и 678 се изчисляват за по-малко от 1 секунда, но


изчисляването на простите множители на 12345 и 23768 е значително по-бавно.

РЕЗЮМЕ

В тази глава видяхте примери за това как да използвате някои полезни и


гъвкави bash команди. Изучавахте примери за скриптове на обвивка за различни
задачи, включващи рекурсия, като изчисляване на GCD и LCM на две положителни
цели числа, стойността на Фибоначи на положително цяло число и простите
делители на положително цяло число.
Machine Translated by Google

ГЛАВА 9

SHELL СКРИПТОВЕ С КОМАНДИТЕ grep И


awk

T
неговата глава съдържа набор от bash скриптове, които илюстрират как да
решаване на някои добре познати задачи. Моля, уверете се, че сте прочели предишната
глава, отнасяща се до командата, ако още не сте го направили.
grep

Първата част на тази глава ви показва набор от bash скриптове, които използват awk
за изпълнение на различни задачи, като например конвертиране на многоредови записи в
едноредови записи. Ще научите също как да изчислявате общата сума на всеки ред в набор
от данни.

Втората част на тази глава ви показва как да покажете основния


диагонални и извъндиагонални стойности, както и сумата от тези стойности.
Въпреки че някои от шел скриптовете в тази глава може да нямат незабавна стойност
за вас, все пак си струва да отделите време да ги прочетете, за да видите дали съдържат
техники, които можете да използвате във вашите собствени шел скриптове.

КОМАНДАТА GREP

Първият пример в този раздел илюстрира как да определите кои zip файлове съдържат
SVG документи. Вторият пример в този раздел ви показва как да проверявате записите в
регистрационен файл (със симулирани стойности). Третият примерен код ви показва как да
използвате командата за симулиране на релация
grep
Machine Translated by Google

база данни, състояща се от три „таблици“, всяка от които е представена от набор от


данни.
Листинг 9.1 показва съдържанието на myzip.sh , което създава два списъка с
файлове: първият списък съдържа имената на zip файловете, които съдържат SVG
документи, а вторият списък съдържа имената на zip файловете, които не съдържат
SVG документи.

ОБЯВКА 9.1: myzip.sh

намерен списък=""
notfoundlist=""

for f in `ls *zip` do found=`unzip -v


$f |

grep "svg$"` if [ "$found" != "" ] then #echo "$f съдържа SVG


документи:" #echo "$ намерен"

foundlist="$f ${foundlist}" else notfoundlist="$f ${notfoundlist}" fi

Свършен

echo "Файлове, съдържащи SVG документи:" echo $foundlist|


'
tr ' '\n'

echo "Файлове, които не съдържат SVG документи:" echo


$notfoundlist |tr ' ' '\n'

Листинг 9.1 търси zip файлове за твърдо кодирания низ svg: ръчно заменете този
низ с всеки път, когато искате да търсите набор от zip файлове за различен низ. Като
алтернатива можете да подканите потребителите за низ за търсене, така че да не е
необходимо да правите ръчни промени в скрипта на обвивката.
За ваше удобство листинг 9.2 показва съдържанието на searchstrings.sh , което
илюстрира как да въведете един или повече низове в командния ред, за да търсите
тези низове в zip файловете в текущата директория.

ЛИСТИНГ 9.2: searchstrings.sh

намерен списък=""
notfoundlist=""
Machine Translated by Google

if [ "$#" == 0 ] then echo "Usage:


$0

<string-list>" изход

фи

zipfiles=`ls *zip 2>/dev/null`

if [ "$zipfiles" = then echo "*** Няма ""]


zip

файлове в 'pwd' ***" изход

фи

за str в "$@" do echo

"Проверка на zip файлове за $str:"


за f в `ls *zip`
do

found=`unzip -v $f |grep "$str"` if [ "$found" != "" ] then


foundlist="$f ${foundlist}" else

notfoundlist="$f ${notfoundlist}" fi

Свършен

echo "Файлове, съдържащи $str:" echo $foundlist| tr ' '\n'


'

echo "Файлове, които не съдържат $str:" echo $notfoundlist |tr '


' '\n' foundlist="" notfoundlist="" done

Списък 9.2 първо проверява дали поне един файл е указан в командния
ред и след това инициализира променливата zipfiles със списъка на zip
файловете в текущата директория. Ако zipfiles е null, се показва подходящо съобщение.
Следващият раздел на листинг 9.2 съдържа for цикъл, който обработва
всеки аргумент, зададен в командния ред. За всеки такъв аргумент друг for
цикъл проверява имената на zip файловете, които съдържат този аргумент. Ако
има съвпадение, тогава променливата $foundlist се актуализира, в противен
случай се актуализира променливата $notfoundlist . Когато вътрешният цикъл приключи,
Machine Translated by Google

имената на съвпадащите файлове и несъвпадащите файлове се показват и след това


външният цикъл се изпълнява със следващия аргумент на командния ред.
Въпреки че предходното обяснение може да изглежда сложно, пример
резултат от стартирането на листинг 9.2 ще изясни как работи кодът:

./searchstrings.sh svg abc Проверка на


zip файлове за svg: Файлове, съдържащи
svg:

Файлове, които не съдържат svg: shell-


programming-manuscript.zip shell-progr-
manuscript-0930-2013.zip shell-progr-
manuscript-0207-2015.zip shell-prog-manuscript.zip
Проверка на zip файлове за abc:
Файлове, съдържащи abc :

Файлове, които не съдържат abc: shell-


programming-manuscript.zip shell-progr-
manuscript-0930-2013.zip shell-progr-
manuscript-0207-2015.zip shell-prog-manuscript.zip

Ако искате да извършите търсене на zip файлове в поддиректории, променете


цикъла, както е показано тук:

за f в „намери“. -print |grep "zip$"` do

echo "Търсене на $f..." разархивиране


-v $f |grep "svg$" готово

Ако имате Java SDK на вашата машина, можете също да използвате командата jar
вместо командата за разархивиране , както е показано тук:

jar tvf $f |grep "svg$"

Списък 9.3 показва съдържанието на skutotals.sh , който изчислява


брой продадени единици за всяка SKU в skuvalues.txt.

ОБЯВКА 9.3: skutotals.sh

SKUVALUES="skuvalues.txt"
SKUSOLD="skusold.txt"
Machine Translated by Google

за sku в `cat $SKUVALUES` do total=`cat $SKUSOLD |grep $sku


|

awk '{общо += $2} КРАЙ

{print total}''` echo


„ПРОДАДЕНИ ЕДИНИЦИ ЗА SKU $sku: $total“ готово

Списък 9.3 съдържа for цикъл, който обикаля редовете на файла skuvalues.txt и
предава тези SKU стойности, една по една, на команда, която включва командите cat,
grep и awk . Целта на последната комбинация от команди е да изпълни следното:

1. Намерете съвпадащите редове в skusold.txt.

2. Изчислете сумата от стойностите на числата във втората колона.


3. Отпечатайте междинната сума за текущата SKU.

По същество този скрипт на обвивката отпечатва междинните суми за всяка SKU


стойност. Стартирайте skutotals.sh и ще видите следния резултат:

ПРОДАДЕНИ БРОЙКИ ЗА SKU 4520: 27


ПРОДАДЕНИ БРОЙКИ ЗА SKU 5530: 17
ПРОДАДЕНИ БРОЙКИ ЗА SKU 6550: 8
ПРОДАДЕНИ БРОЙКИ ЗА SKU 7200: 90
ПРОДАДЕНИ ЕДИНИЦИ ЗА SKU 8000: 160

Можем да обобщим предишния шел скрипт, за да вземем предвид различната цена


за всяка SKU. Листинг 9.4 показва съдържанието на
skuprices.txt.

ОБЯВКА 9.4: skuprices.txt

4520 3,50 5530


5,00 6550 2,75

7200 6.25
8000 3,50

Листинг 9.5 показва съдържанието на skutotals2.sh , което разширява кода в


Списък 9.3 за изчисляване на приходите за всяка SKU.

ОБЯВКА 9.5: skutotals2.sh

SKUVALUES="skuvalues.txt"
Machine Translated by Google

SKUSOLD="skusold.txt"

SKUPRICES="skuprices.txt"

за sku в `cat $SKUVALUES` направете skuprice=`grep

$sku $SKUPRICES | cut -d" " -f2' subtotal=`cat $SKUSOLD |grep $sku | awk '{общо += $2}

КРАЙ {печат общо}''


total=`echo "$subtotal * $skuprice" |bc` echo "ПРОДАДЕНА СУМА ЗА SKU $sku:
$total" done

Списък 9.5 съдържа леко подобрение: вместо да се изчисляват междинните


суми на броя на единиците за всяка SKU, се изчисляват приходите за всяка
SKU , където приходите за всеки артикул са равни на цената на SKU , умножена
по броя на продадените единици за даден SKU. Стартирайте skutotals2.sh и ще
видите следния резултат:

ПРОДАДЕНО КОЛИЧЕСТВО ЗА SKU 4520: 94,50


ПРОДАДЕНО КОЛИЧЕСТВО ЗА SKU 5530: 85,00
ПРОДАДЕНО КОЛИЧЕСТВО ЗА SKU 6550: 22.00
ПРОДАДЕНО КОЛИЧЕСТВО ЗА SKU 7200: 562,50
ПРОДАДЕНО КОЛИЧЕСТВО ЗА SKU 8000: 560,00

Списък 9.6 показва съдържанието на skutotals3.sh , което изчислява


минималния, максималния, средния и общия брой продадени единици за всяка SKU в
skuvalues.txt.

ОБЯВКА 9.6: skutotals3.sh

SKUVALUES="skuvalues.txt"
SKUSOLD="skusold.txt"

TOTALS="totalspersku.txt" rm -f $TOTALS 2>/dev/


null

############################## #изчисляване на
общите суми за всяка sku ###############
################ за sku в `cat $SKUVALUES` do total=`cat
$SKUSOLD |grep $sku | awk '{total += $2} END {print

total}'` echo "ПРОДАДЕНИ ЕДИНИЦИ ЗА SKU $sku: $total" echo "$sku|$total" >> $TOTALS готово
Machine Translated by Google

########################## #изчислете
макс./мин./средно ##################
######### awk -F"|" '

НАЧАЛО {първо = 1;} {ако


(първо) { min = max= avg = сума = $2; първо=0; следващ}} { if($2 < min) { min = $2 } if($2 > max) { max
= $2 } сума += $2

}
КРАЙ {print "Minimum = ",min print "Maximum =
",max print "Average = ",avg print "Total
= ",сума

}
$ TOTALS

Списък 9.6 инициализира някои променливи, последвани от for цикъл, който извиква
awk команда за изчисляване на междинни суми (т.е. брой продадени единици) за всяка SKU
стойност. Следващата част от листинг 9.6 съдържа команда awk , която изчислява максимума,
минимума, средната стойност и сумата за SKU единиците във файловете $TOTALS.

Стартирайте скрипт файла в листинг 9.6 и ще видите следния изход:

ПРОДАДЕНИ БРОЙКИ ЗА SKU 4520: 27


ПРОДАДЕНИ БРОЙКИ ЗА SKU 5530: 17
ПРОДАДЕНИ БРОЙКИ ЗА SKU 6550: 8
ПРОДАДЕНИ БРОЙКИ ЗА SKU 7200: 90
ПРОДАДЕНИ ЕДИНИЦИ ЗА SKU 8000: 160
Минимум = 8
Максимум = 160
Средно = 27
Общо = 302

СИМУЛИРАНЕ НА РЕЛАЦИОННИ ДАННИ С КОМАНДАТА GREP

Този раздел ви показва как да комбинирате grep и команди за изрязване ,

за да следите малка база данни от клиенти, техните покупки и подробности за техните


покупки, които се съхраняват в три текстови файла.
Има много налични инструменти с отворен код, които могат значително да улеснят
работата с релационни данни и нерелационни данни. Тези комплекти инструменти могат
да бъдат стабилни и да минимизират необходимото количество кодиране.
Machine Translated by Google

Освен това можете да използвате командата за присъединяване (обсъдена в глава 2), за да


извършвате подобни на SQL операции върху набори от данни. Независимо от това, истинската
цел на този раздел е да илюстрира някои техники, които могатgrep
да бъдат полезни във вашите
собствени шел скриптове.
Листинг 9.7 показва съдържанието на текстовия файл MasterOrders.txt .

ОБЯВА 9.7: MasterOrders.txt

M10000 C1000 15.12.2012 г


M11000 C2000 15.12.2012 г
M12000 C3000 15.12.2012 г

Листинг 9.8 показва съдържанието на текстовия файл Customers.txt .

ОБЯВА 9.8: Customers.txt

C1000 Джон Смит Лос Алтос Калифорния 94002


C2000 Джейн Дейвис MountainView Калифорния 94043

C3000 Billy Jones HalfMoonBay Калифорния 94040

Листинг 9.9 показва съдържанието на текстовия файл PurchaseOrders.txt .

ОБЯВА 9.9: PurchaseOrders.txt

C1000,"Радио",54.99,2,"01/22/2013"
C1000,"DVD",15.99,5,"01/25/2013"
C2000,"Лаптоп",650.00,1,"01/24/2013"
C3000,"Мобилен телефон",150.00,2,"01/28/2013"

Списък 9.10 показва съдържанието на bash скрипта MasterOrders.sh .

ОБЯВКА 9.10: MasterOrders.sh

# инициализиране на променливи за трите основни файла


MasterOrders="Главни поръчки.txt"
CustomerDetails="Customers.txt"
PurchaseOrders="Поръчки за покупка.txt"

# итерация през "главната таблица" за mastCustId в `cat $MasterOrders


| cut -d" " -f2` do # вземете информацията за клиента custDetails=`grep $mastCustId $CustomerDetails`

# вземете идентификатора от предишния ред


Machine Translated by Google

custDetailsId=`echo $custDetails | изрежете -d" " -f1`

# вземете поръчката на клиента от PO файла custPO=`grep $custDetailsId


$PurchaseOrders`

# отпечатайте подробностите за клиента echo "Customer


$mastCustId:" echo "Customer Details: $custDetails"
echo "Purchase Orders: $custPO" echo "------------------ ----"

ехо
Свършен

Списък 9.10 инициализира някои променливи за поръчки, подробности и набори от


данни, свързани с покупката. Следващата част от листинг 9.10 съдържа for цикъл, който
обикаля стойностите на id (които са във втората колона) във файла MasterOrders.txt и
използва всеки id , за да намери съответния ред във файла Customers.txt , както и съответния
ред във файла PurchaseOrders.txt . И накрая, долната част на цикъла показва подробностите
за информацията, която е извлечена от началната част на цикъла for .

Резултатът от листинг 9.10 е тук:

Клиент C1000:
Подробности за клиента: C1000 John Smith LosAltos California 94002 Поръчки за покупка:
C1000,"Radio",54.99,2,"01/22/2013"
C1000,"DVD",15.99,5,"01/25/2013"
----------------------

Клиент C2000: Подробности


за клиента: C2000 Jane Davis MountainView Калифорния 94043 Поръчки за покупка: C2000,"Лаптоп",650.00,1,"01/24/2013"

----------------------

Клиент C3000:

Подробности за клиента: C3000 Billy Jones HalfMoonBay Калифорния 94040 Поръчки за покупка:
C3000,"CellPhone",150.00,2,"01/28/2013"
----------------------

ПРОВЕРКА НА АКТУАЛИЗАЦИИТЕ В ЛОГ ФАЙЛ

Листинг 9.11 показва съдържанието на CheckLogUpdates.sh , което илюстрира как


периодично да проверявате последния ред в регистрационен файл, за да определите
състоянието на системата. Този скрипт на обвивката симулира състоянието на системата
чрез добавяне на нов ред, който се основава на текущия времеви печат. Скриптът на обвивката заспива за a
Machine Translated by Google

определен брой секунди, а на третата итерация скриптът добавя ред със


състояние на грешка, за да симулира грешка. В случай на шел скрипт, който
наблюдава активна система, кодът за грешка очевидно се генерира извън
шел скрипта.

ОБЯВКА 9.11: CheckLogUpdates.sh

DataFile="mylogfile.txt"
OK="добре"
ERROR="грешка"

sleeptime="2" loopcount=0

rm -f $DataFile 2>/dev/null; докоснете $DataFile newline="`date` СИСТЕМАТА Е ДОБРЕ"


echo $newline >> $DataFile while (true) do loopcount=`expr
$loopcount + 1`

echo "спи $sleeptime секунди..." sleep $sleeptime echo "събуден


отново..."

lastline=`tail -1 $DataFile`

if [ "$lastline" == continue fi "" ] тогава

okstatus=`echo $lastline |grep -i $OK` badstatus=`echo $lastline |grep -i


$ERROR`

if [ "$okstatus" != "" ] then echo "системата е

нормална" if [ $loopcount -lt 5 ] then


newline="`date` СИСТЕМАТА Е ДОБРЕ"

else
newline="`date` СИСТЕМНА ГРЕШКА"
fi
echo $newline >> $DataFile
elif [ "$badstatus" != "" ] then echo "Грешка в

регистрационния файл: $lastline" break


Machine Translated by Google

фи
Свършен

Листинг 9.11 инициализира някои променливи и след това гарантира, че лог файлът mylogfile.txt
е празен. След добавяне на начален ред към този журнален файл, цикълът while периодично заспива
и след това проверява съдържанието на последния ред текст в журналния файл. Към този лог файл се
добавят нови текстови редове и когато се открие съобщение за грешка, кодът излиза от цикъла while .
Примерно извикване на листинг 9.11 е тук:

спи 2 секунди… отново е буден…


системата е нормално
спи 2 секунди… буден отново…
системата е нормално спи 2
секунди… отново е
буден… системата е нормално
спи 2 секунди… отново е буден…
системата е нормално
спи 2 секунди… отново е
буден… системата е нормална спя
2 секунди... отново
съм буден...

Грешка в регистрационния файл: четвъртък, 23 ноември, 18:22:22 PST 2017 СИСТЕМНА ГРЕШКА

Съдържанието на регистрационния файл е показано тук:

Чет, 23 ноември, 18:22:12 PST 2017 СИСТЕМАТА Е ДОБРЕ


Чет, 23 ноември, 18:22:14 PST 2017 г. СИСТЕМАТА Е ДОБРЕ
Чет, 23 ноември, 18:22:16 PST 2017 г. СИСТЕМАТА Е ДОБРЕ
Чет, 23 ноември, 18:22:18 PST 2017 г. СИСТЕМАТА Е ДОБРЕ
Чет, 23 ноември, 18:22:20 PST 2017 СИСТЕМАТА Е ДОБРЕ
Чет, 23 ноември, 18:22:22 PST 2017 СИСТЕМНА ГРЕШКА

ОБРАБОТКА НА МНОГОРЕДОВИ ЗАПИСИ

Листинг 9.12 показва съдържанието на набора от данни multiline.txt. Списък 9.13 показва
съдържанието на шел скрипта multiline.sh , който комбинира множество редове в един запис.
Machine Translated by Google

ОБЯВКА 9.12: multiline.txt

Мери Смит
999 Апиев път
Римски град, SF 94234

Джейн Адамс
Главна улица 123
Чикаго, IL 67840

Джон Джоунс
321 Pine Road
Навсякъде, MN 94949

Имайте предвид, че всеки запис обхваща няколко реда, които могат да съдържат бели
интервали, и записите са разделени с празен ред.

ЛИСТИНГ 9.13: multiline.sh

# Записите са разделени с празни редове awk '

""
НАЧАЛО { RS = { ; FS = "\n" }

gsub(/[ \t]+$/, "", $1) gsub(/[ \t]+$/, "", $2)


gsub(/[ \t]+$/, "", $3)

gsub(/^[ \t]+/, "", $1) gsub(/^[ \t]+/, "", $2)


gsub(/^[ \t]+/, "", $3)

печат $1 ":" $2 ":" $3 "" #printf("%s:%s:%s\n",


$1,$2,$3)

} ' multiline.txt

Списък 9.13 съдържа блок BEGIN , който задава стойността на RS („разделител на


запис“) равна на празен низ и също така задава стойността на FS („разделител на поле“)
като преместване на ред. Това ни позволява да „вмъкнем“ няколко реда в един и същи
запис, като използваме празен ред като разделител за различни записи. Функцията gsub()
премахва водещи и завършващи бели интервали и раздели за три полета в наборите от
данни. Резултатът от стартирането на листинг 9.13 е тук:

Мери Смит: 999 Appian Way: Римски град, SF 94234


Джейн Адамс: Главна улица 123: Чикаго, Илинойс 67840
John Jones: 321 Pine Road: Anywhere, MN 94949
Machine Translated by Google

ДОБАВЯНЕ НА СЪДЪРЖАНИЕТО НА ЗАПИСИ

Листинг 9.14 показва съдържанието на набора от данни numbers.txt. Листинг 9.15


показва съдържанието на шел скрипта sumrows.sh , който комбинира (т.е. добавя
стойностите на) полетата във всеки запис.

ОБЯВКА 9.14: numbers.txt

12345
6 7 8 9 10
55555

ОБЯВКА 9.15: sumrows.sh

awk '{ for(i=1; i<=NF;i++) j+=$i; печат j; j=0 }' numbers.txt

Листинг 9.15 съдържа просто извикване на командата awk , която


съдържа for цикъл, който използва променливата j за задържане на сумата от стойностите на
полетата във всеки запис, след което се отпечатва сумата и j се инициализира отново
до 0. Резултатът от листинг 9.15 е тук:

15
40
25

ИЗПОЛЗВАНЕ НА ФУНКЦИЯТА SPLIT В AWK

Списък 9.16 показва съдържанието на набора от данни genetics.txt (някои


редовете се пренасят в повече от една линия). Листинг 9.17 показва съдържанието на
скриптът на обвивката genetics.sh , който използва функцията split() за анализиране на
съдържание на поле в запис.

ЛИСТИНГ 9.16: genetics.txt

#извличане на редове с „ген“ и отпечатване на редове и стойност „ключ“.


xyz3 GTF2GFF chro xyz3 55555 44444 ключ=chr1;Име=chr1
GTF2GFF генен 77774 11111
ключ=XYZ123;NB=стандартен;Име=допълнително

xyz3 GTF2GFF екзон 71874 12227 Супер=NR_55555


xyz3 GTF2GFF екзон 72613 12721 Супер=NR_55555
xyz3 GTF2GFF екзон 83221 14408 Супер=NR_55555
Machine Translated by Google

xyz3 GTF2GFF генен 84362 29370


ключ=WASH7P;Забележка=допълнително;Име=ALPHA

xyz3 GTF2GFF екзон 84362 14829 Супер=NR_222222

ЛИСТИНГ 9.17: genetics.sh

# изискван изход:
#xyz3:77774:XYZ123
#xyz3:84362:WASH7P

awk -F" " ' {

if( $3 == "gene") { split($6, triplet, /


[;=]/) printf("%s:%s:%s\n", $1, $4, triplet[2] )

} ' genetics.txt

Списък 9.17 съвпада с входни редове, чието трето поле е равно на gen, след
което триплетът на масива се попълва с компонентите на шестото поле, като се
използват знаците „;“ и “=” като разделители в шестото поле. Резултатът се състои от
първото поле, четвъртото поле и втория елемент в триплета на масива. Резултатът
от стартирането на листинг 9.17 е тук:

xyz3:77774:XYZ123
xyz3:84362:WASH7P

СКАНИРАНЕ НА ДИАГОНАЛНИ ЕЛЕМЕНТИ В НАБОРИ ДАННИ

Листинг 9.18 показва съдържанието на набора от данни diagonal.csv. Листинг


9.19 показва съдържанието на скрипта на обвивката diagonal.sh , който показва
елементите в главния диагонал и извън диагонала и също така изчислява сумата на
елементите в главния диагонал и извън диагонала.

ОБЯВКА 9.18: diagonal.csv

1,1,1,1,1
5,4,3,2,1
8,8,1,8,8
5,4,3,2,1
1,6,6,7,7
Machine Translated by Google

ОБЯВКА 9.19: diagonal.sh

# NF е броят на полетата в текущия запис.


# NR е номерът на текущия запис/ред # (не броят на записите във файла).

# В блока END (или последния ред на файла) # това е броят на редовете във файла.

# Решение в R: https://gist.github.com/dsparks/3693115

echo "Основен диагонал:" awk -F","


'{ for (i=0; i<=NF; i++) if (NR >= 1 && NR == i) print $(i) }' diagonal.csv

echo "Извън диагонала:" awk -F","


'{print $(NF+1-NR)}' diagonal.csv

echo "Основна диагонална сума:" awk -F","


'

НАЧАЛО {сума = 0} {

за (i=0; i<=NF; i++) { if (NR >= 1 && NR == i) { сума += $i } }


}
КРАЙ { printf ("sum = %s\n",sum) } ' diagonal.csv

echo "Извън диагонална сума:" awk -F","


'

НАЧАЛО {сума = 0} {

за (i=0; i<=NF; i++) { if(NR >= 1 && i+NR == NF+1) { сума += $i; } } }

КРАЙ { printf ("sum = %s\n",sum) } ' diagonal.csv

Списък 9.19 започва с команда awk , която съдържа цикъл, който съответства
на „диагонални“ елементи на набора от данни, което ще рече първото поле на
първия запис, второто поле на втория запис, третото поле на третия запис и така
нататък. Този процес на съвпадение се управлява от условната логика вътре в
цикъла for .
Втората част на списък 9.19 съдържа команда awk , която отпечатва
недиагонални елементи на набора от данни, като се използва проста инструкция за печат .
Третата част на листинг 9.19 съдържа awk команда, която съдържа същата
логика като първата awk команда и след това изчислява кумулативната сума на
диагоналните елементи.
Machine Translated by Google

Четвъртата част на листинг 9.19 съдържа команда awk , която съдържа


логика, която е подобна на първата awk команда, със следния вариант:

ако (NR >= 1 && i+NR == NF+1)

Предходната логика ни позволява да изчислим кумулативната сума на изкл.


диагонални елементи. Резултатът от стартирането на листинг 9.19 е тук:

Главен диагонал:

41
2
7
Извън диагонала: 1

2
1

41

Сума по главния диагонал: сума


= 15
Сума извън диагонала: сума
=9

Листинг 9.20, листинг 9.21 и листинг 9.22 показват съдържанието на наборите


от данни rain1.csv, rain2.csv и rain3.csv.txt , използвани в няколко скрипта на
обвивката в този раздел.

ОБЯВКА 9.20: rain1.csv

1,0.10,53,15
2,0.12,54,16
3,0.19,65,10
4,0.25,86,23
5,0.18,57,17
6,0.23,79,34
7,0.34,66,21

ОБЯВКА 9.21: rain2.csv

1,0.00,63,24
2,0.02,64,25
3,0.09,75,19
Machine Translated by Google

4,0.15,66,28
5,0.08,67,36
6,0.13,79,23
7,0.24,68,25

ОБЯВКА 9.22: rain3.csv

1,1.00,83,34
2,0.02,84,35
3,1.09,75,19
4,0.15,86,38
5,1.08,87,36
6,0.13,79,33
7,0.24,88,45

ДОБАВЯНЕ НА СТОЙНОСТИ ОТ МНОГО НАБОРА ДАННИ (1)

Листинг 9.23 показва съдържанието на шел скрипта rainfall1.sh , който


добавя числата в съответните полета на няколко CSV файла и показва
резултатите.

ОБЯВКА 9.23: дъжд1.ш

# => Изчислете средните стойности на КОЛОНА за множество файлове

#columns in rain.csv: #DOW, инчове


дъжд, градуси F, влажност (%)

#files: rain1.csv, rain2.csv, rain3.csv echo "ИМЕНА НА ФАЙЛОВЕ:" ls rain?.csv

awk -F',' ' {

инча+=$2
градуса+=$3
влажност+=$4
}
КРАЙ
{ printf("ИМЕ НА ФАЙЛ: %s\n", ИМЕ НА ФАЙЛ) printf("инчове:
%.2f\n", инчове/7) printf("градуси: %.2f\n", градуси/7) printf(" влажност:
%.2f\n", влажност/7)

} ' дъжд?.csv
Machine Translated by Google

Листинг 9.23 изчислява сумата от числата в три колони (т.е. инчове валежи,
градуси по Фаренхайт и влажност като процент) в наборите от данни, определени от
израза rain?.csv, който в този конкретен пример се състои от наборите от данни rain1.
csv, rain2.csv и rain3.csv.
По този начин листинг 9.23 може да обработва множество набори от данни
(rain1.csv до rain9.csv). Можете да обобщите този пример, за да обработвате всеки
набор от данни, който започва с низа rain и завършва с наставката csv със следния
израз:

дъжд*.csv

Резултатът от стартирането на листинг 9.23 е тук:

ИМЕНА НА ФАЙЛОВЕ:

rain1.csv rain2.csv инча: 0,83 дъжд3.csv

градуси: 217.71 влажност:


79.43

ДОБАВЯНЕ НА СТОЙНОСТИ ОТ МНОЖЕСТВО НАБОРА ДАННИ (2)

Листинг 9.24 показва съдържанието на шел скрипта rainfall12.sh , който добавя


числата в съответните полета на няколко CSV файла и показва резултатите.

ОБЯВКА 9.24: rainfall2.sh

# => Изчислете средните стойности на ROW за множество файлове

#columns in rain.csv: #DOW, инчове


дъжд, градуси F, влажност (%)

#файлове: rain1.csv, rain2.csv, rain3.csv

awk -F',' ' {

mon_rain[FNR]+=$2
mon_degrees[FNR]+=$3
mon_humidity[FNR]+=$4 idx[FNR]++

}
END
{ printf("ДЕН ИНЧОВ ГРАДУСИ ВЛАЖНОСТ\n")
Machine Translated by Google

for(i=1; i<=FNR; i++){ printf("%3d %-6.2f


%-8.2f %-7.2f\n",

i,mon_rain[i]/idx[i],mon_degrees[i]/idx[i],mon_humidity[i]/idx[i])
}

} ' дъжд?.csv

Списък 9.24 е подобен на листинг 9.23, с изключение на това, че този примерен


код използва стойността на FNR за изчисляване на средните валежи, градуси по
Фаренхайт и процентна влажност само за понеделник. Резултатът от стартирането на
листинг 9.24 е тук:

ДЕН ИНЧИ ГРАДУСИ ВЛАЖНОСТ


1 0,37 66,33 2 0,05 67,33 3 24.33
0,46 71,67 25.33
16.00ч
4 0,18 79,33 29,67
5 0,45 70,33 6 0,16 79,00 7 29,67
0,27 74,00 30,00
30,33

Листинг 9.25, листинг 9.26 и листинг 9.27 показват съдържанието на наборите от


данни zain1.csv, zain2.csv и zain3.csv , използвани в предстоящ скрипт на обвивка в този
раздел.

ОБЯВКА 9.25: zain1.csv

1,0.10,53,15 2,0.12,54,16
3,0.19,65,10 4,0.25,86,23
5,0.18,57,17 6,0.23,79,34
7,0.34,66,21

ОБЯВКА 9.26: zain2.csv

1,0.00,63,24
2,0.02,64,25 3,0.09,75,19
4,0.15,66,28 5,0.08,67,36
6,0.13,79,23 7,0.24,68,25
Machine Translated by Google

ОБЯВКА 9.27: zain3.csv

1,1.00,83,34
2,0.02,84,35
3,1.09,75,19
4,0.15,86,38
5,1.08,87,36
6,0.13,79,33 7,0.24,88,45

ДОБАВЯНЕ НА СТОЙНОСТИ ОТ МНОЖЕСТВО НАБОРА ДАННИ (3)

Листинг 9.28 показва съдържанието на шел скрипта rainfall3.sh , който добавя


числата в съответните полета на няколко CSV файла и показва резултатите.

ОБЯВКА 9.28: дъжд 3.sh

# => Изчислете средните стойности на КОЛОНА за множество файлове (обратно)

#columns in rain.csv: #DOW, инчове


дъжд, градуси F, влажност (%)

# посочете списъка с CSV файлове (поддържа множество регулярни изрази) files=`ls rain*csv zain*csv`

echo "ФАЙЛОВЕ: `echo $files`"

awk -F',' ' {

mon_rain[FNR]+=$2
mon_degrees[FNR]+=$3
mon_humidity[FNR]+=$4 idx[FNR]++

}
END
{ printf("ДЕН ИНЧОВ ГРАДУСИ ВЛАЖНОСТ\n")

for(i=1; i<=FNR; i++){ printf("%3d %-6.2f


%-8.2f %-7.2f\n",

i,mon_rain[i]/idx[i],mon_degrees[i]/idx[i],mon_humidity[i]/idx[i])
}

} ' `echo $files`


Machine Translated by Google

Списък 9.28 извършва същите изчисления като листинг 9.24, със следната вариация:
наборите от данни, определени от променливите файлове , които се дефинират от
регулярния израз 'ls rain*csv zain*csv'. Можете да модифицирате този регулярен израз,
за да включи всеки списък с файлове, които трябва да бъдат обработени.
Забележете, че последният ред код в листинг 9.28 използва заместване с обратна
отметка, за да разшири регулярния израз в дефиницията на променливите файлове:

' `ехо $файлове`

Като още един вариант, можете да посочите файл (да го наречем filelist.txt) , който
съдържа списък с имена на файлове, които искате да обработите, и след това да
замените предходния ред, както следва:

' `cat filelist.txt`

Резултатът от стартирането на листинг 9.28 е тук:

ФАЙЛОВЕ: rain1.csv rain2.csv rain3.csv zain1.csv zain2.csv zain3.csv


ДЕН ИНЧИ ГРАДУСИ ВЛАЖНОСТ
1 0,37 66,33 24.33
2 0,05 67,33 3 0,46 71,67 4 25.33
0,18 79,33 16.00
29.67
5 0,45 70,33 29,67
6 0,16 79,00 7 0,27 74,00 30.00
30.33

ИЗЧИСЛЯВАНЕ НА КОМБИНАЦИИ ОТ СТОЙНОСТИ НА ПОЛЕТА

Списък 9.29 показва съдържанието на shell скрипта linear-combo.sh , който изчислява


различни линейни комбинации от колоните в множество набори от данни и показва
един комбиниран набор от данни като изход.

ОБЯВКА 9.29: linear-combo.sh

# => комбинации от колони awk -F',' ' {

$2 += $3 * 2 + $4 / 2 $3 += $4 / 3 + $2 *
$2 / 10 $4 += $2 + $3 $1 += $2 * 3 - $4 / 10
Machine Translated by Google

printf("%d,%.2f,%.2f,%.2f\n",$1,$2,$3,$4)

} ' дъжд?.csv

Листинг 9.29 обработва стойностите на наборите от данни rain1.csv, rain2.csv и


rain3.csv, чието съдържание е показано по-рано в този раздел. Основното
наблюдение, което трябва да се направи, е, че последователността от изчисления
в изчисленията в тялото на оператора awk включва взаимозависимости.
По-конкретно, стойността на $2 е линейна комбинация от стойностите на $3 и
$4. След това стойността на $3 е линейна комбинация от стойността на $4 и $2,
където последната не е оригиналната стойност от наборите от данни, а нейната
изчислена стойност. Трето, стойността на $4 е линейна комбинация от $2 и $3, като
и двете са изчислени стойности, а не стойностите в наборите от данни.
И накрая, стойността на $1 е линейна комбинация от новоизчислените стойности за $2 и $4.

Както можете да видите, awk предоставя гъвкавостта да укажете практически


всяка комбинация от изчисления (включително нелинейни комбинации) по прост
и последователен начин. Резултатът от листинг 9.30 е тук:

194,113.60,1348.50,1477.10
196,116.12,1407.72,1539.84
204,135.19,1895.97,2041.16
187,183.75,3470.07,3676.82 202,122.68,1567
.70,1707.38 194,175.23,3160.89,3370.12
207,142.84,2113.33,2277.17
201,138.00,1975.40,2137.40
202,140.52,2046.92,2212.44 201,1 59.59,
2628.23, 2806.82 203,146.15,2211.32,2385.47
203,152.08,2391.83,2579.91
199,169.63,2964.10,3156.73
206,148.74,2288.69,2462.43 183,184.00,3479
.93,3697.93 182,185.52,3537.43,3757.95
200,160.59,2660.25,2839.84
179,191.15,3752.50,3981.65
178,193.08,3826.99,4056.07 195,1 74.63,
3139.56, 3347.19 173,198.74,4052.76,4296.50

РЕЗЮМЕ
Machine Translated by Google

В тази глава видяхте примери как да създадете някои полезни bash команди. Първо
видяхте bash скрипт за работа с текстови файлове, съдържащи многоредови записи. След
това видяхте как да изчислявате кумулативни суми на числови полета в записите. След
това научихте как да използвате функцията split() вътре в shell скрипт с команда awk .

Научихте как да използвате awk за обработка на записи, които обхващат множество


набори от данни за изчисляване на средни и общи стойности. Задачите с множество
набори от данни ви позволиха да научите по-сложни шел скриптове, които работят върху
набор от „релационни“ таблици, представени под формата на текстови файлове. Накрая
научихте как динамично да изчислявате различни комбинации от колони с числа от
множество набори от данни.
Machine Translated by Google

ГЛАВА 10

РАЗНИ SHELL СКРИПТОВЕ

T
неговата глава съдържа еклектичен асортимент от bash скриптове, които илюстрират
как да изпълнявате различни задачи, включващи множество файлове и директории,
работа с компресирани файлове, фонови процеси и отпечатване на прости отчети.

Първата част на тази глава ви показва как избирателно да копирате и изтривате


файлове от дърво на директории. Втората част на тази глава съдържа набор от шел
скриптове, които създават поддиректории в директория въз основа на набор от низове.
Един от скриптовете ви показва как да проверите дали даден низ е съществуваща
поддиректория или файл.
Третата част на тази глава обсъжда различни команди за работа с zip файлове, като
gzip и gunzip. (Вижте глава 4 за примери на командата tar за създаване на архивни
файлове.) Четвъртата част на тази глава ви показва как да планирате задачи с помощта
на командите at и crontab. Накрая ще научите за командите, свързани с печата, и как да
генерирате прости отчети.

ИЗПОЛЗВАНЕ НА RM И MV С ДИРЕКТОРИИ

Преди да разгледаме скриптовете на обвивката, има някои прости задачи, които


можете да изпълните с командата rm . Например, ако искате да премахнете директорията
my_stuff и цялото й съдържание, можете да го направите с тази команда:

rm -r моите_неща
Machine Translated by Google

Като алтернатива можете да използвате командата mv , за да преместите директорията


my_stuff в директорията /tmp :

mv my_stuff /tmp

Въпреки това, предходната команда работи най-много веднъж; ако директорията


вече съществува в /tmp, тогава предходната команда mv ще бъде неуспешна.
Ако искате да клонирате цялото съдържание на директорията my_stuff (включително
всички поддиректории) в директорията my_stuff2, използвайте тази команда:

cp -r my_stuff my_stuff2

Можете също да използвате cp или mv команда във връзка с back


заместване за копиране (или преместване) на подмножество от файлове в множество поддиректории.

Да предположим например, че имате директория с множество поддиректории, които


съдържат документи на Word и текстови файлове, както е показано тук:

myfiles/wordfiles/chapter1/chapter1.doc myfiles/wordfiles/chapter2/
chapter2.doc myfiles/wordfiles/chapter3/chapter3.doc myfiles/
wordfiles/chapter4/chapter4.doc myfiles/wordfiles/chapter1/data1.txt
myfiles/wordfiles/chapter2 /data2.txt myfiles/wordfiles/chapter3/
data3.txt myfiles/wordfiles/chapter4/data4.txt

Листинг 10.1 показва съдържанието на shell скрипта maketree.sh , който създава набор от
поддиректории в текущата директория.

ОБЯВКА 10.1: maketree.sh

# премахнете myfiles rm -r
myfiles 2>/dev/null

# създаване на поддиректории mkdir


-p myfiles/wordfiles/chapter1 mkdir -p myfiles/wordfiles/
chapter2 mkdir -p myfiles/wordfiles/chapter3 mkdir -p myfiles/
wordfiles/chapter4

# създаване на празни файлове


touch myfiles/wordfiles/chapter1/chapter1.doc touch myfiles/wordfiles/chapter2/
chapter2.doc
Machine Translated by Google

докоснете myfiles/wordfiles/chapter3/chapter3.doc
докоснете myfiles/wordfiles/chapter4/chapter4.doc
докоснете myfiles/wordfiles/chapter1/data1.txt
докоснете myfiles/wordfiles/chapter2/data2.txt
докоснете myfiles/wordfiles/chapter3/data3.txt
докоснете myfiles/wordfiles/chapter4/data4.txt

Списък 10.1 премахва директорията myfiles и след това използва mkdir -p


команда за създаване на набор от поддиректории, последвана от командата докосване
за създаване на празни документи на Word и празни текстови файлове.

ИЗПОЛЗВАНЕ НА КОМАНДАТА FIND С ДИРЕКТОРИИ

Първо се уверете, че сте стартирали скрипта на обвивката mktree.sh в


предходния раздел, за да създадете указаната структура на директория с
определени файлове.

Следната команда намира всички документи на Word в myfiles


директория:

намери myfiles -име "*.doc"

Следната команда копира всички документи на Word от myfiles


директория към директорията /tmp :

cp `find myfiles -name "*.doc"` /tmp

Можете да извършвате подобни операции с текстовите файлове чрез замяна


срещания на *.doc с *.txt.

Алтернативно, следната команда използва комбинация от cp, find,


и команди
grep за копиране на всички документи на Word от myfiles
директория към директорията /tmp :

cp `намери моите файлове |grep "\.doc$"` /tmp

Забележете шаблона "\.doc$" в командата grep , който гарантира, че само


файлове със суфикс .doc се намират от командата. Този модел
grep
предотвратява съвпадението на файла (или директорията) mydoc_stuff или mydoc в
grep команда.

Въпреки това е възможно да се създаде директорията , наречена my.doc, която ще


също съвпадат в командата.
grep В този сценарий командата ще cp
показва съобщение за грешка, че директорията my.doc не е копирана.
Machine Translated by Google

СЪЗДАВАНЕ НА ДИРЕКТОРИЯ НА ДИРЕКТОРИИ

Листинг 10.2 показва съдържанието на шел скрипта makedirs.sh , който създава


набор от поддиректории в директорията с имена .

ОБЯВКА 10.2: makedirs.sh

#################################################### # # Дефиниране
на променлива със списък с имена на директории # Този метод не зависи от
външен файл.
# Алтернативно, поставете тези имена в текстов файл # и след това прочетете
съдържанието на този текстов файл.
#################################################### #

# променете стойността на mydir на каквото ви трябва mydir="names"

name_list="andrew-webber dave-jones jane-smith john-smith keith-thompson"

if [ then ! -d $mydir]
mkdir

$mydir cd $mydir

за име в `echo $name_list`


do

echo "създаване на директория $name в $mydir" mkdir $name done else echo
"Директория $mydir

съществува" fi

Листинг 10.2 инициализира променливата name_list с твърдо кодиран списък от


имена, всяко от които става поддиректория на $mydir. Следващата част от листинг
10.2 съдържа логика if/else за определяне дали директорията $mydir съществува. Ако
не стане, тогава цикъл преминава през низовете в $name_list и създава съответна
поддиректория на $mydir.
Стартирайте кода в листинг 10.2 и ще видите следния резултат:

създаване на директория andrew-webber в имена създаване на


директория dave-jones в имена създаване на директория jane-
smith в имена създаване на директория john-smith в имена
създаване на директория keith-thompson в имена
Machine Translated by Google

Въпреки това, ако извикате кода в листинг 10.2 втори път, ще го направите
вижте следния изход:

Съществуват имена на директории

КЛОНИРАНЕ НА НАБОР ПОДДИРЕКТОРИИ

Списък 10.3 показва съдържанието на имената на директориите , а листинг


10.4 показва съдържанието на скрипта на обвивката clonedirs1.sh , който попълва
директорията names2 с директориите в директорията с имена .

ЛИСТИНГ 10.3: имена

Джон-Смит Джейн-
Смит Дейв-Джоунс
Андрю-Уебър

кийт-томпсън

ОБЯВКА 10.4: clonedirs1.sh

if [ then ! -d имена2]
echo

"създаване на директория 'имена'" mkdir names2

фи

cd имена2

echo "inside directory names2" за d в `ls ../names` do

echo "Създаване на директория $d" mkdir $d готово

Списък 10.4 съдържа прост for цикъл, който обикаля съдържанието на


имената на директорията (брат) , показва съобщение и създава съответна
директория в директорията names2 .
Стартирайте кода в листинг 10.4 и ще видите следния резултат:

Създаване на директория andrew-webber


Създаване на директория dave-jones
Създаване на директория jane-smith
Machine Translated by Google

Създаване на директория john-smith


Създаване на директория keith-thompson

Въпреки това, ако извикате кода в листинг 10.4 втори път, ще го направите
вижте множество грешки, тъй като директориите вече съществуват, както е показано тук:

Създаване на директория andrew-webber mkdir: andrew-


webber: Файлът съществува

Създаване на директория dave-jones mkdir: dave-jones:


Файлът съществува
Създаване на директория jane-smith mkdir: jane-smith:
Файлът съществува
Създаване на директория john-smith mkdir: john-smith:
Файлът съществува
Създаване на директория keith-thompson mkdir: keith-thompson:
Файлът съществува

Списък 10.5 показва съдържанието на скрипта на обвивката clonedirs2.sh , който проверява


дали вече съществува директория, преди да попълни имената на директорията2 с директориите
в директорията с имена .

ОБЯВКА 10.5: clonedirs2.sh

ако ! -d имена2]
[ тогава

echo "създаване на 'имена' на директория" mkdir names2

фи

cd имена2

echo "inside directory names2"

for d в `ls ../names` do if [ -d $d ] then echo

"Директория $d вече

съществува" else echo "Създаване на директория $d" mkdir $d fi

Свършен

Списък 10.5 съдържа прост for цикъл, който обикаля съдържанието на имената на
директориите (сродници). Кодовият блок if /else проверява дали
Machine Translated by Google

директорията вече съществува; ако е така, се показва съобщение; ако не,


директорията се създава в директорията names2 .
Стартирайте кода в листинг 10.5 и ще видите следния резултат:

Директорията andrew-webber вече съществува


Директория dave-jones вече съществува
Директория jane-smith вече съществува
Директория john-smith вече съществува
Директория keith-thompson вече съществува

Списък 10.6 показва съдържанието на шел скрипта clonedirs3.sh , който първо


проверява дали даден файл в names2 е файл вместо директория: ако е така, не се
създава директория. След това, ако файлът в names2 всъщност е директория, кодът
проверява дали вече съществува директория, преди да попълни директорията
names2 с директориите в директорията names .

ОБЯВКА 10.6: clonedirs3.sh

if [ then ! -d имена2]
echo

"създаване на директория 'имена'" mkdir names2

фи

cd names2 echo

"inside directory names2"

for d в `ls ../names` do if [ -f $d ] then echo


"$d
е файл (не директория)"

elif [ -d $d ] then echo

"Директория $d вече съществува" else

echo "Създаване на директория $d" mkdir $d fi

Свършен

Списък 10.6 съдържа прост for цикъл, който обикаля съдържанието на имената
на директориите (сродници). Кодовият блок if /else проверява дали
Machine Translated by Google

директорията вече съществува; ако е така, се показва съобщение; ако не, директорията
се създава в директорията names2 .
Стартирайте кода в листинг 10.6 и ще видите следния резултат:

Директорията andrew-webber вече съществува


Директория dave-jones вече съществува
Директория jane-smith вече съществува
Директория john-smith вече съществува
Директория keith-thompson вече съществува

Списък 10.7 показва съдържанието на шел скрипта copy-file1.sh , който първо


проверява дали даден файл в names2 е файл вместо директория: ако е така, не се
създава копие. След това, ако файлът в names2 всъщност е директория, кодът копира
file1 в тази директория. Ако файлът не съществува, кодът създава директория и копира
file1 в директорията.

ОБЯВКА 10.7: copy-file1.sh

echo "здравей свят" >/tmp/file1 file1="/tmp/file1"

за d в „ls“.
do
if [ -f $d ] then echo

"Пропускане на команда за копиране за $d" elif [ -d $d ] then echo


"Копиране на $file1 в $d"
else

echo "Създаване на директория $d" mkdir $d echo

"Копиране на $ file1 в $d" fi

Свършен

Списък 10.7 съдържа прост for цикъл, който обикаля съдържанието на имената на
директориите (сродници). Кодовият блок if /else проверява дали директорията вече
съществува; ако е така, се показва съобщение; ако не, директорията се създава в
директорията names2 .
Стартирайте кода в листинг 10.7 и ще видите следния резултат:

Копиране на /tmp/file1 в andrew-webber Пропускане на


команда за копиране за clone-dirs1.sh
Machine Translated by Google

Пропускане на команда за копиране за clone-dirs2.sh Пропускане на команда


за копиране за clone-dirs3.sh Пропускане на команда за копиране за copy-
file1.sh Копиране на /tmp/file1 в dave-jones Копиране на /tmp/file1 в jane-
smith Копиране на /tmp/file1 в john-smith Копиране на /tmp/file1
в keith-thompson

ИЗПЪЛНЕНИЕ НА ФАЙЛОВЕ В МНОЖЕСТВО ДИРЕКТОРИИ

Да предположим, че имате изпълними шел скриптове в множество директории


и искате да изпълните всички тях. Едно решение включва следене на директориите
и изпълнимите файлове във всяка от тези директории и след това навигиране до
тези директории за извършване на необходимото действие.
Списък 10.8 показва съдържанието на шел скрипта remove-files.sh , който
премахва различни файлове въз основа на текущата директория.

ОБЯВКА 10.8: remove-files.sh

# следете горната директория topdir=`pwd`

за f в "ls"

направете if [ -d $f ] then
cd

$topdir/$f

if [ -f "remove.sh" ] then echo "изпълнявам

remove.sh в $f" sh remove.sh

else
echo "не може да намери remove.sh в $f" fi

cd $topdir fi

Свършен

Листинг 10.8 присвоява текущата директория на променливата topdir, последвана


от цикъл, който обработва съдържанието на поддиректориите на topdir. Обърнете
внимание на логиката if/else, за да се уверите, че текущата стойност на $f всъщност
е директория.
Machine Translated by Google

Ако $f е директория, изпълнете команда cd в тази директория. Обърнете внимание, че


вътрешната логика if/else проверява дали шел скриптът remove.sh съществува, преди да
изпълни този шел скрипт.
Друг момент, който трябва да забележите, е началният sh в командата remove.sh (показан
с удебелен шрифт), който ни позволява да изпълняваме команди, дори ако те не трябва да
изпълняват разрешения.
Сега стартирайте кода в листинг 10.8 и ще видите следния резултат:

изпълнение на remove.sh в заглавки


премахване на текстови
файлове изпълнение на remove.sh в
src премахване на обектни файлове

КОМАНДАТА НА СЛУЧАЯ/ESAC

Както вече знаете, командата case/esac ви позволява да извършвате различни действия,


които се основават на стойността на въведеното от потребителя. Само за опресняване на
паметта ви, ето примерен синтаксис на оператора case :

case $option in 1) echo


"Стартиране на архивиране на системата...";; 2)
echo "Четене на лентово устройство...";; x)
echo "Наистина ли си сигурен? (Да/не)"
esac

Най-общо ще покажете меню с опции и ще подканите потребителите за техния избор,


след което ще изпълните съответната команда.
Списък 10.9 показва съдържанието на шел скрипта case-menu.sh , който показва набор
от опции и подканва потребителите да въвеждат.

ЛИСТИНГ 10.9: case-menu.sh

меню() {

echo "1) Извършете архивиране на системата" echo "2)


Прочетете лентовото устройство" echo "x)
Излезте от системата" echo "" echo
"Въведете

опция:"
}
Machine Translated by Google

process_option() {

case $option in 1) echo


"Стартиране на архивиране на системата...";; 2) echo "Четене на
лентово устройство...";; x) echo "Наистина ли сте сигурни?
(Y/n)" read val val=$val | tr '[:upper:]' '[:lower:]'

if [ "$val" = "y" ] then echo "Излизане


от

системата … довиждане" изход

fi ;;
*) echo "Излизане от системата ... сбогом" изход ;;

esac
}

докато (вярно)
правя
меню

прочетете опцията
process_option
Свършен

Листинг 10.9 дефинира функцията menu() , която показва набор от опции, последвана
от функцията process_option() , която обработва входа, предоставен от потребителите.

Отговорът за стойност 1 или стойност 2 е ясен и трябва да вмъкнете код, който


действително ще направи нещо.
Отговорът за стойността x е по-сложен и илюстрира гъвкавостта на оператора case/
esac . Можете да изпълните всичко, което трябва да направите в тази опция. В този
пример входната стойност се преобразува в стойност с малки букви: ако е равна на
х, Забележете, че програмата съществува и за
малки букви, тогава програмата излиза.
всички други входни стойности.
Последната част от листинг 10.9 съдържа цикъл, който 1) показва менюто, 2)
подканва потребителите за входна стойност и 3) обработва въведената от потребителите
стойност.
Стартирайте кода в списък 10.9 и изберете всички опции на менюто, както е
показано тук:

1) Извършете архивиране на системата


2) Прочетете лентовото
устройство x) Изход от системата
Machine Translated by Google

Въведете опция: 1

Стартиране на архивиране на системата...


1) Извършете архивиране на системата 2)
Прочетете лентовото устройство
x) Излезте от системата

Въведете опция: 2

Четене на лентово устройство…


1) Извършете архивиране на системата 2)
Прочетете лентовото устройство
x) Излезте от системата

Въведете опция:
х

наистина ли си сигурен (да/не)

1) Извършете архивиране на системата


2) Прочетете лентовото
устройство x) Изход от системата

Въведете опция: 4

Излизане от системата

КОМПРЕСИРАНЕ И ДЕКОМПРЕСИРАНЕ НА ФАЙЛОВЕ

Този кратък раздел съдържа описание на набор от команди за компресиране


и декомпресиране на файлове:

Компресиране: Компресиране на файлове


gunzip: Декомпресирайте gzip файлове gzip:
GNU алтернативен метод за компресиране декомпресиране:
Декомпресиране на файлове разархивиране: архив
Избройте, тествайте и извличайте компресирани файлове в ZIP

zcat: Cat компресиран файл


zcmp: Сравнете компресираните файлове
zdiff: Сравнете компресираните файлове
zmore: Филтър за преглеждане на файлове за crt преглеждане на компресиран текст

Помощната програма uuencode кодира двоични файлове (като изображения,


звукови файлове и компресирани файлове) в ASCII знаци, което ги прави
подходящи за предаване като прикачен файл или дори в тялото на имейл съобщение. Това
Machine Translated by Google

е особено полезно, когато MIME (мултимедийно) кодиране не е налично. Uudecode


декодира файловете, създадени чрез командата uuencode .

КОМАНДАТА DD

Страницата с ръководство за командата dd описва dd като помощна програма, която


„копира стандартен вход към стандартен изход“. Въпреки това безобидно описание,
командата dd всъщност е доста мощна и дори може да преобразува ASCII символи в
EBCDIC, който е формат на данни за мейнфрейми и обратно. Синтаксисът на командата
dd е както следва:

dd if=ИЗТОЧНИК на=ЦЕЛ bs=BLOCK_SIZE брой=БРОЙ

Опциите if и of в предходния фрагмент са съответно за входния файл и изходния


файл. Опцията bs е за размера на блока (обикновено кратно на 512), а count е цяло число,
което указва броя на блоковете за копиране. Имайте предвид, че bs и count не са
задължителни. Ако count е пропуснато, тогава dd копира данни от входния файл, докато
достигне маркера за край на файла (EOF).

За да копирате дял във файл, използвайте тази команда:

dd if=/dev/sda1 of=sda1_partition.img

Имайте предвид, че /dev/sda1 в предходния фрагмент е пътя на устройството за дяла.

Възстановете дяла с помощта на архива с тази команда:

dd if=sda1_partition.img of=/dev/sda1

Генерирайте файла data.file от 100 kb, както следва:

dd if=/dev/нула bs=100k count=1 of=data.file

Предходната команда създава файл data.file , който е пълен с нули с размер 100 kb.

Следващата команда създава файла junk.data с размер точно 1 MB:

dd if=/dev/zero of=junk.data bs=1M count=1 1+0 записа в


Machine Translated by Google

1+0 записва 1048576 байта

(1.0 MB) копирани, 0.00767266 s, 137 MB/s

КОМАНДАТА КРОНТАБ

Командата crontab ви позволява да планирате изпълнението на задачите редовно.


Например, можете да планирате изпълнението на шел скриптове по гъвкав график,
вместо ръчно да извиквате тези шел скриптове.
Имайте предвид, че задачите на crontab не наследяват средата на конкретен
потребител, което означава, че трябва да сте сигурни, че всички необходими променливи
на средата са зададени правилно (като например извикване на скрипт, който съдържа
тези променливи).
Можете да планирате изпълнение на задача въз основа на следното:


на почасова, дневна или седмична база

определен ден от месеца

определен месец или година

Следната команда показва текущо планираните задания за вашата машина:

crontab

Трябва да сте или root потребител, или да използвате sudo, за да промените crontab (в
/usr/bin директория) със следната команда:

crontab -е

Следващата команда замества текущия набор от задания със заданията в


файла crontab.new:

crontab -r < crontab.new

ДЕКОМПРЕСИРАНЕ НА ФАЙЛОВЕ КАТО ЗАДАЧА НА CRON

Листинг 10.10 показва съдържанието на uncompress.sh , което илюстрира как


за декомпресиране на набор от „zip“ файлове в отделни директории.

СПИСЪК 10.10 uncompress.sh


Machine Translated by Google

#!/bin/sh

zipfiles="`ls *zip`"

if [ "$zipfiles" != "" ] then for f in `echo $zipfiles`


do echo

"Обработване на файл: $f" f1=`echo $f |

изрежете -d"." -f1` f2=`ехо $f | изрежете -d"." -f2`


newdir="${f1}-нов"

echo "Създаване на директория: $newdir" mkdir -p $newdir cp $f


$newdir cd $newdir

echo "Разкомпресиране на файл: $f" # командата jar е


включена в разпространението на Java: #jar xvf $f

# използвайте командата разархивирайте, ако нямате инсталирана Java: разархивирайте $f

# връщане към родителската директория: cd ../ done

друго

echo "Няма намерени zip файлове" fi

Листинг 10.10 инициализира променливата zipfiles със списъка на zip файловете в


текущата директория, последван от блок if/else , който проверява дали съществуват
някакви zip файлове чрез проверка на стойността на променливата zipfiles.
Ако zipfiles не е празен, for цикъл преминава през всеки файл в променливата
zipfiles и след това конструира низ, наречен newdir , като добавя низа -new към
префикса f1 на текущото име на файл.
Следващият раздел в листинг 10.10 извиква командата mkdir -p , за да създаде
нова директория с име newdir. След извикване на командата cd за влизане в тази нова
директория, текущият zip файл (който е стойността на променливата f на цикъла) се
декомпресира в тази директория. Забележете извикването на cd .. (което е последният
оператор в цикъла) за връщане към родителската директория.

ПЛАНИРАНИ КОМАНДИ И ФОНОВИ ПРОЦЕСИ


Machine Translated by Google

Разделът съдържа набор от bash команди, които могат да бъдат полезни за различни задачи и

скриптове на shell. Можете да планирате изпълнение на команда в определено време чрез командата
at или чрез cron. Можете също така да планирате изпълнение на команда във фонов режим чрез
символа амперсанд („&“).

Как да планирате задачи

Командата за управление на заданието изпълнява даден набор от команди в определено


време. Повърхностно прилича на cron; въпреки това, командата at е основно полезна за еднократно
изпълнение на набор от команди. Например, следният фрагмент подканва за набор от команди,
които да бъдат изпълнени по това време:

в 14:00 часа на 15 януари

Тези команди трябва да са съвместими с shell-скрипт, тъй като потребителите въвеждат в


изпълним shell скрипт ред по ред. Въвеждането завършва с a
ctrl-d.

Използвайте или опцията -f , или пренасочване на въвеждане (<), когато имате нужда от
командата at , за да прочетете списък с команди от файл.

Командата за управление на пакетно задание е подобна на командата at ; въпреки това


изпълнява списък с команди, когато системното натоварване е по-малко от .8. Командата batch може
също да чете команди от файл с опцията -f .

Командата nohup

Командата nohup („без прекъсване“) ви позволява да изпълните друга команда и да продължите


да изпълнявате тази команда дори след като сте излезли. Ето един прост пример:

nohup myscript.sh

По подразбиране и стандартният изход, и стандартната грешка (ако има такива) се пренасочват


към текстовия файл nohup.out в текущата директория.

Изпълнение на команди от разстояние

Bash поддържа отдалечено изпълнение на различни команди. Можете дистанционно да


влезете в друга машина с помощта на командата rsh . Ако искате да използвате защитена връзка,
използвайте ssh вместо rsh.
Machine Translated by Google

След като влезете в отдалечена система, можете да изпълнявате различни команди между двете
системи. Например rcp е аналог на командата. Други команди включват rsync, ftp, ping и telnet. cp
Извършете онлайн търсене, за да научите подробностите за тези команди.

Как да планирате задачи във фонов режим

Превключвателят & изпълнява процес във фонов режим. Имайте предвид, че &
не понижава приоритета на текущия изпълняващ се процес. Тази опция е удобна за
извикване на две команди в една и съща обвивка:

намирам . -name "*txt" -print > text_files & tail -f text_files

Предходната команда за намиране се изпълнява във фонов режим, тъй като


търси всички файлове със суфикс txt и пренасочва изхода към text_files. Командата
tail показва съдържанието на text_files всеки път, когато този файл се актуализира.
Можете да изведете предишния фонов процес на преден план с тази команда:

fg %1

КАК ДА ПРЕКРАТЯВАТЕ ПРОЦЕСИТЕ

Командата kill <number> PID ви позволява да прекратите задачи или процеси,


като посочите стойност на сигнала за <number> и идентификатор на процес за PID.
Стойността на <число> може да бъде 1, 2, 3, 6, 9, 14 или 15.
Например, и двете от следните команди ще прекратят процес със сигнал за
прекъсване:

kill -1 2345 kill -s


HUP 2345

Можете да прекратите процес, чийто PID е 2345 с kill -3 2345 и kill -9 2345, които са
съответно сигнали QUIT и KILL . Сигналът QUIT обаче може да бъде „уловен” чрез
командата „trap”, докато сигналът KILL принуждава процеса да прекрати незабавно
(т.е. веднага щом операционната система може да изпълни тази команда). Освен това
сигналът KILL не може да бъде уловен чрез командата “trap” и не може да бъде
игнориран.
Machine Translated by Google

ЗАБЕЛЕЖКА Само суперпотребител може да изпрати сигнал до процеси на друг потребител.

Извършете онлайн търсене за повече подробности относно командата kill ,


или проверете man страницата с тази команда:

човек убива

Прекратяване на множество процеси

PID командата kill <number> ви позволява да прекратите множество


процеси, като посочите списък с процеси, пример за който е показан
тук:

убийте -1 5000 5010 5135 68238

Друг сценарий включва множество дъщерни процеси, които имат същото


родителски процес. Например, когато стартирате няколко сесии на браузъра в
Chrome или Firefox, всяка сесия на браузъра ще има свързан PID. Там
са два начина за прекратяване на всички дъщерни процеси и родителския процес. един

техниката е да прекратите родителския процес, който се появява на монитора


полезност. Въпреки това, ако трябва да прекратите тези процеси от командата
ред, можете да извикате следната команда:

ps -ef |grep -i firefox | awk '{print $2}' | xargs убиват -9

КОМАНДИ, СВЪРЗАНИ С ПРОЦЕСА

The
пс командата показва информация за процесите на вашата машина.
Например, ако въведете следната команда от командния ред:

ps -ef | повече | глава -7

ще видите резултат, подобен на следния:

UID PID PPID C STIME TTY ВРЕМЕ CMD


0 1 0 0 Четвъртък05 сутринта ?? 34:15.35 /sbin/launchd
0 12 1 0 Четвъртък05 сутринта ?? 0:10.58 /usr/libexec/kextd
14 0 Четвъртък05 сутринта ?? 1:44.60 /usr/sbin/notified
15 11 0 Четвъртък05 сутринта ?? 2:18.59 /usr/sbin/securityd
0 0 -i
Machine Translated by Google

0 17 1 0 Четвъртък05 сутринта ?? 46:41.08 /usr/libexec/configd


0 18 1 0 Четвъртък05 сутринта ?? 1: 26.37 /usr/sbin/syslogd

Командата kill ви позволява да прекратите процеси. Например, на


следната команда прекратява браузъра Chrome, който работи на вашия
машина:

убийте -9 Chrome

Ако изпълнявате команди в шел скрипт, можете да намерите идентификатора на процеса на


текущо изпълняващият се процес от $$, докато $! съдържа ID на процеса
на процеса, който наскоро премина на заден план.
Командата uptime показва колко дълго работи системата и
примерен резултат е тук:

23:21 до 1 ден, 12:59, 9 потребители, средни стойности на натоварване: 2,60 3,26 3,42

КАК ДА НАБЛЮДАВАМЕ ПРОЦЕСИТЕ

Горната команда показва информация за изпълняваните в момента задачи,


като „най-добрите“ потребители на процесора, количество памет, което те консумират и
приоритети на процесите. Примерен изход от горната команда е тук:

Процеси: общо 322, 2 работещи, 320 спящи, 1382 нишки


21:35:32
Средно натоварване: 2,08, 1,69, 1,44 Използване на процесора: 3,85% потребител, 2,65% система,
93,49% празен ход
SharedLibs: 142 милиона резиденти, 42 милиона данни, 22 милиона свързани редактирани.
MemRegions: общо 85784, 2587M резидентни, 57M частни, 577M споделени.
PhysMem: 8135M използван (1543M кабелен), 55M неизползван.
VM: 1331G всички размери, 634M рамка всички размери, 48085(0) swapins, 136883(0)
замени.
Мрежи: пакети: 3488298/4488M in, 1873020/198M out.
Дискове: 2042619/22G четене, 346447/14G записване.
% ЦП ВРЕМЕ #TH #WQ #PORT MEM PID КОМАНДА ПРЕЧИСТВАНЕ

CMPRS PGRP PPID


5207 отгоре 3.9 00:00.67 1/1 0 20 2892K+ 0B 0Б
5207 491
5092 чаши d 5092 1 0,0 00:00,06 3 1 43 2420K 0B 0Б

5089- Office365Ser 0.0 00:00.21 8 5089 1 2 154 4676K 0B 0Б

5084 quicklookd 0.0 00:00.16 4 5084 1 1 86 4344K 32K 0Б


Machine Translated by Google

5043- mdworker32 0.0 00:00.46 3 1 51 5772K 0B 0Б


5043 1
4989 mdworker 4989 1 0,0 00:00,57 4 1 48 21M 0Б 0Б

4943 netbiosd 0,0 00:00,06 2 2 30 500K 0B


1884K 4943 1

Извършете онлайн търсене за повече подробности относно опциите, които са


наличен за горната команда.

ПРОВЕРКА НА РЕЗУЛТАТИТЕ ОТ ИЗПЪЛНЕНИЕТО

Всеки път, когато трябва да определите състоянието на най-скоро изпълнените


команда, проверете стойността на $?. На този термин се приписва стойността, която е
зададен от оператора за изход на най-скоро изпълнената команда. от
конвенция, изходна стойност 0 показва успешно изпълнение, а различна от нула
стойностите показват неуспешен резултат.
Списък 10.11 показва съдържанието на parent.sh , което илюстрира как да
проверете резултата от изпълнението на шел скрипт от друг шел скрипт.

ОБЯВКА 10.11 parent.sh

#проверете резултата от shell скрипта

ехо "първи път:"


./cmdargs.sh паста
echo "резултат = $?"

echo "втори път:"

./cmdargs.sh
echo "резултат = $?"

echo "трети път:"

cmdargs.sh
echo "резултат = $?"

Листинг 10.11 извиква shell скрипта cmdargs.sh три пъти: първият


време с параметър паста, а последващите извиквания с бр
параметри. Всеки път, когато се стартира cmdargs.sh , стойността на $? е
показан в текущия скрипт на обвивката, което е изходното състояние на cmdargs.sh.
Листинг 10.12 показва съдържанието на cmdargs.sh , което илюстрира как да
проверете резултата от изпълнението на шел скрипт от друг шел скрипт.
Machine Translated by Google

ОБЯВКА 10.12 cmdargs.sh

if [ $# -eq 0 ] then echo


"Usage:

$0 <filename>" exit 1

фи

изход 0

Списък 10.12 проверява дали са посочени някакви аргументи на командния


ред, когато cmdargs.sh се стартира от командния ред. Ако не, се показва
съобщение и шел скриптът завършва с изходна стойност 1. В противен случай
шел скриптът завършва с изходна стойност 0.
Стартирайте предходния скрипт на обвивката, както следва:

./cmdargs.sh

Резултатът е показан тук:

първи път:
резултат = 0 втори
път: Използване: ./

cmdargs.sh <име на файл> резултат = 1

трети път:

Използване: ./cmdargs.sh <име на файл> резултат = 1

СИСТЕМНИ СЪОБЩЕНИЯ И ЛОГ ФАЙЛОВЕ

Bash предоставя командата dmesg за показване на съобщения, свързани със системата. Примерен изход от командата

dmesg е тук:

път на изображението за хибернация: /var/vm/sleepimage


AirPort_Brcm43xx::powerChange: Заспиване на системата hibernate_alloc_pages act
865005, inact 768286, anon 589882, throt 0, spec 30749, wire 407619, wireinit 241382 hibernate_setup(0) отне 0 ms
sizeof(IOHibernateImage Header) == 512 kern_open_file_for_direct_io(0) отне 249 ms

Отворен файл /var/vm/sleepimage, размер 8589934592, дял база 0x0, maxio 400000 ssd 0 хибернация изображение
основно 1, второстепенно 0, размер на
блокове 512, избиратели 5 en1: BSSID променен на 68:7f:74:cb:05:f8
Machine Translated by Google

wlEvent: en1 en1 Link DOWN virtIf = 0


AirPort: Link Down на en1. Причина 8 (Деасоциация поради напускане на станцията). en1::IO80211Interface::postMessage bssid
променен LE се
поддържа - Деактивиране на LE мета събитие 0 [Време 1380032186] [Съобщение
hibernate_page_list_setall(preflight 0) начало 0xffffff80fb3d5000,
0xffffff80fb433000

Директорията /var на вашата машина съдържа различни файлове и директории. Пример за


съдържанието му е тук:

en1::IO80211Interface::postMessage bssid променен общо 10872=

-rw-r--r--@ 1 root CDIS.custom drwxrwx--- колело 12 12 септември 2012 г


33 root
DiagnosticMessages администратор 1122 24 септември 02:11

Както можете да видите, root потребителят е собственик на много от тези директории, така че
трябва да използвате sudo, за да видите съдържанието на тези директории.
Част от уместно наречения файл /var/log/zzz.log е тук:

Сряда, 12 септември 14:22:43 201 [SleepServicesD]


/SourceCache/SleepServicesD_executables/SleepServicesD-1.43/SleepServicesD/
ModeConfig.m:41 Изчаква се зареждане на IOPlatformPluginFamily...

Сряда, 12 септември 14:22:48 201 [SleepServicesD]


/SourceCache/SleepServicesD_executables/SleepServicesD-1.43/SleepServicesD/
ModeConfig.m:41 Изчаква се зареждане на IOPlatformPluginFamily...

Сряда, 12 септември 14:22:49 201 [SleepServicesD]


/SourceCache/SleepServicesD_executables/SleepServicesD-1.43/SleepServicesD/
ModeConfig.m:41 Изчаква се зареждане на IOPlatformPluginFamily...

Команди за използване на диска

Командата du показва информация за „използване на диска“, а следното извикване показва


броя на килобайтите за файловете в текущата директория:

ду -с -к .

Заменете -k с -m , за да видите броя на мегабайтите.


Machine Translated by Google

Командата df показва наличното „свободно дисково“ пространство и пример


извикването е тук:

Файлова система 1024-блокове %iused Използван наличен капацитет iused ifree


Монтирани на
/dev/disk1 487184384 462012772 24915612 95% / 95% 115567191 6228903

Командата df с опцията -h произвежда този тип изход:

Файлова система Използван размер Наличен Използван капацитет ifree


%iused Монтирана на
/dev/disk1 / 893Gi 800Gi 92Gi 90% 17489436 4277477843 0%

devfs / 191Ki 191Ki 0Bi 100% 661 0 100%


dev
map -hosts /net 0Bi 0Bi 0Bi 100% 0 0 100%

карта auto_home / 0Bi 0Bi 0Bi 100% 0 0 100%


начало

УЛАВЯНЕ И ИГНОРИРАНЕ НА СИГНАЛИ

Листинг 10.13 показва съдържанието на trap1.sh , което илюстрира как да прихващате


сигнал в bash. В този пример се показва съобщение, когато потребителите променят размера
тяхната командна обвивка за по-малко от 10 секунди.

СПИСЪК 10.13 trap1.sh

#!/bin/sh

maxCount="10"
смяна на екрана()
{
echo "Уловен сигнал от командата за прихващане"
}

echo "Преоразмеряване на текущата командна обвивка."

капан за промяна на екрана SIGWINCH

БРОЯ=0
докато [$COUNT -lt $maxCount]; направи
БРОЙ=$(($БРОЙ + 1))
сън 1
Свършен
Machine Translated by Google

Листинг 10.13 инициализира променливата maxCount със стойност 10, последвана


от дефиницията на функцията за промяна на екрана , която просто показва съобщение
чрез командата echo .
След това командата echo подканва потребителите да преоразмерят своята командна
обвивка, последвана от командата trap , която извиква функцията за промяна на екрана , ако
командната обвивка бъде преоразмерена за по-малко от 10 секунди.

Последната част от листинг 10.13 е for цикъл, който повтаря 10 пъти (т.е.
стойността, присвоена на променливата maxCount) и заспива за една секунда по време
на всяка итерация.
Списък 10.14 показва съдържанието на trap2.sh , което илюстрира как да
игнорирате сигнал в шел скрипт.

ЛИСТИНГ 10.14: trap2.sh

#!/bin/sh
"" ЗНАК
капан

echo "Тази програма ще заспи за 10 секунди и не може да бъде убита с"

ехо "control-c."
сън 10

Списък 10.14 започва с командата за прихващане , която „прихваща“ сигнала


SIGINT , който се появява, когато натиснете ctrl-c в командния ред. Следващата част от
списък 10.14 показва съобщение и след това влиза в режим на заспиване за 10 секунди.
Стартирайте кода в списък 10.14 и опитайте да прекратите програмата, като натиснете
ctrl-c в командния ред.

АРИТМЕТИКА С КОМАНДИТЕ BC И DC

Bash shell не прави разлика между низове и числа (цели числа или реални числа).
Въпреки това, командата bc ви позволява да работите с числа, примери за които са
показани тук:

bc
bc 1.06

Copyright 1991-1994, 1997, 1998, 2000 Free Software Foundation, Inc.

Това е безплатен софтуер без АБСОЛЮТНО НИКАКВА ГАРАНЦИЯ.

За подробности напишете 'гаранция'. 4+3


Machine Translated by Google

7
4,5 + 3,7
8.2

Като алтернатива можете да въведете следните команди от командния ред:

echo "2+3" |bc 5 echo

"4.5+3.7" |bc 8.2

Можете да присвоявате стойности на променливи и да извършвате аритметични операции


с командата bc , както е показано тук:

х=4

y = 7 echo
"$x * $y" |bc 28

РАБОТА С КОМАНДАТА ДАТА

Командата date предоставя информация за текущата дата в различни формати, като


посочва различни опции на командния ред. Командата date може да бъде полезна за
генериране на квазислучайно име на файл. Като алтернатива можете също да използвате
текущия идентификатор на процес, който се съхранява в $$, както и командата tempfile
за генериране на имена на файлове.

#!/bin/bash #
Упражняване на командата 'date' echo "Броят дни от
началото на годината е 'date +%j'."
# Нуждае се от водещ '+' за извикване на форматиране. # %j дава ден от годината.
echo "Броят секунди, изминали от
01/01/1970 е 'дата +%s'." # %s дава брой секунди от началото на "BASH епохата", #+ но как е полезно това?

prefix=temp suffix=$
(date +%s) # Опцията "+%s" за 'date' е специфична за GNU. filename=$prefix.$suffix echo "Временно име на файл = $filename"

# Страхотно е за създаване на "уникални и произволни" имена на временни файлове,


#+ дори по-добро от използването на $$.
Machine Translated by Google

$ дата
Четвъртък, 20 май 23:09:04 IST 2010 г

Времето на епохата може да бъде отпечатано, както следва:

$ дата +%s
1290047248

Можете да конвертирате низ от дата в епоха, както следва:

$ date --date "четвъртък, 18 ноември 08:07:21 IST 2010" +%s 1290047841

Опцията --date се използва за предоставяне на низ за дата като вход. Въпреки това можем да използваме

всякакви опции за форматиране на дата, за да отпечатаме изхода. Захранването на въведената дата от низ

може да се използва за намиране на деня от седмицата, като се има предвид датата. Ето един прост пример:

$ дата --дата "20 януари 2001" +%A


Събота

Използвайте комбинация от форматиращи низове с префикс + като аргумент за командата за дата, за да

отпечатате датата във формат по ваш избор. Ето един пример:

$ дата "+%d %B %Y"


20 май 2010 г

Можем да настроим датата и часа, както следва:

date -s "Форматиран низ с дата"

Пример за това как да използвате предходния синтаксис е тук:

дата -s "21 юни 2009 г. 11:01:22"

Списък 10.15 показва съдържанието на DateInfo1.sh , който изпълнява date-

свързана манипулация във връзка с изявление за случай/esac .

ОБЯВКА 10.15 DateInfo1.sh

# скрипт за следното: # показване на деня от


седмицата
Machine Translated by Google

# показване на текущия месец #


изпълнение на някаква команда

TOP=`pwd`
today=`date`
dayOfWeek=`echo $today |cut -d" " -f1` theMonth=`echo $today |
cut -d" " -f2` dateDir="${dayOfWeek}-${ theMonth}" newDir="$TOP/
$dateDir"

echo "Днес: $today" echo "Ден от


седмицата: $dayOfWeek" echo "Месецът:
$theMonth" echo "Директория: $newDir"

ако [! -d $нова директория]; тогава mkdir -p


$newDir fi случай $dayOfWeek
в

Mon) echo "Monday" #execute


command1
;;
Tue) echo "Tuesday" #execute
command2
;;
ср) echo "сряда" #execute command3
докоснете "$newDir/monday-
news"
;;
Thu) echo "Thursday" #execute
command4
;;
Пет) echo "Петък" #изпълни
команда5
;;
Sat) echo "Saturday" #execute
command6
;;
Sun) echo "Sunday" #execute
command7
;;
esac

Листинг 10.15 започва с инициализиране на променливата TOP с текущата директория,


последвано от инициализиране на променливата today с текущата дата.
Следващият раздел в листинг 10.15 инициализира някои свързани с датата променливи, както
е показано тук:

dayOfWeek=`echo $today |cut -d" " -f1`


Machine Translated by Google

theMonth=`echo $today |cut -d" " -f2` dateDir="${dayOfWeek}-


${theMonth}" newDir="$TOP/$dateDir"

След това няколко оператора за ехо показват стойностите на променливите, които са


инициализирани в предходния кодов блок.
Последната част от листинг 10.15 е кодов блок case/esac , който сравнява стойността на
променливата dayOfWeek с всеки ден от седмицата.
Забележете, че всяка възможност има „коментирано“ изявление, което можете да замените
с команда, която искате да изпълните всеки ден от седмицата.

КОМАНДИ, СВЪРЗАНИ С ПЕЧАТА

Свързаните с печата команди ви позволяват да изпълнявате различни задачи, като


например отпечатване на файл с помощта на командата lp , командата pr или командата lpr .
Можете да използвате командата lpstat , за да проверите състоянието на принтер и
планираните задания за печат. Командата lpq показва състоянието на опашката на посочения
принтер.
Имайте предвид, че всеки чакащ или активен файл има присвоен номер на задание за
печат, който можете да посочите, когато извършвате операции с тези файлове, като
например изтриване на файл (обсъдено по-късно). Ако вашата система включва принтер,
ето пример за използване на командата lpr за отпечатване на файл, наречен
myscript.sh:

lpr myscript.sh

Отпечатването на дългите файлове отнема повече време и добрата новина е, че можете


да изпращате допълнителни файлове към принтера, докато файлът се отпечатва. Командата
lpq показва файловете, които са в "опашката за печат":

lpq
lp е готов и се печата
Ранг собственик Файлове за работа Обща сума

Размер
активен корен 155 /etc/passwd 1030
байтове

Използвайте lprm, за да отмените отпечатването на файл, като посочите номера на


заданието за печат на този файл. Ето един пример:

lprm 155
Machine Translated by Google

Предходната команда отменя отпечатването на задание номер 155. Въпреки това, само
потребителят, който е поискал файлът да бъде отпечатан (или root потребителят), може да
отмени отпечатването на файла.

Създаване на отчет с командата printf().

Листинг 10.16 показва съдържанието на SimpleReport.sh , което илюстрира как да


актуализирате набор от потребители.

ОБЯВКА 10.16 SimpleReport.sh

DataFile="users.txt"
NAME="Джон Доу"
ADDRESS="1234 Appian Way, SF"
ТЕЛЕФОН="(555) 555-5555"
GPA="3,885"

# показване на подравнения изход: printf "%20s |


%30s | %14s | %5s\n" "Име" "Адрес" "Телефонен номер"
"GPA"

printf "%20s | %30s | %14s | %5.2f\n" "$NAME" "$ADDRESS" "$PHONE"


"$GPA"

Листинг 10.16 започва с инициализиране на няколко променливи със симулирани данни,


отнасящи се до хипотетичен потребител. Втората част от листинг 10.16 извиква оператора
printf() два пъти, за да покаже свързаната с потребителя информация във формат, подравнен
по колони.
Резултатът от листинг 10.16 е тук:

Име | Адрес | Телефонен номер | GPA Джон Доу | 1234 Main


Street, SF, CA | (555) 555-5555 | 3,88

ПРОВЕРКА НА АКТУАЛИЗАЦИИТЕ В ЛОГ ФАЙЛ

Листинг 10.17 показва съдържанието на CheckLogUpdates.sh , което илюстрира как


периодично да проверявате последния ред в регистрационен файл, за да определите
състоянието на системата. Този скрипт на обвивката симулира състоянието на системата чрез
добавяне на нов ред, който се основава на текущия времеви печат.
Скриптът на обвивката заспива за определен брой секунди и на третата итерация скриптът
добавя ред със състояние на грешка, за да симулира
Machine Translated by Google

грешка. В случай на шел скрипт, който наблюдава активна система, кодът


за грешка очевидно се генерира извън шел скрипта.

ОБЯВКА 10.17 CheckLogUpdates.sh

DataFile="mylogfile.txt"
OK="добре"
ERROR="грешка"

sleeptime="2" loopcount=0

rm -f $DataFile 2>/dev/null; докоснете $DataFile newline="`date` СИСТЕМАТА Е ДОБРЕ"


echo $newline >> $DataFile

while (true) do

loopcount='expr $loopcount + 1'

echo "спи $sleeptime секунди..." sleep $sleeptime echo "събуден


отново..."

lastline='tail -1 $DataFile'

if [ "$lastline" == then continue fi ""]

okstatus=`echo $lastline |grep -i $OK` badstatus=`echo $lastline |grep -i


$ERROR`

if [ "$okstatus" != "" ] then echo "системата е

нормална" if [ $loopcount == "3" ] then


newline="`date` СИСТЕМАТА Е ДОБРЕ"

else
newline="`date` СИСТЕМНА ГРЕШКА"
fi
echo $newline >> $DataFile
elif [ "$badstatus" != "" ] then echo "Грешка в

регистрационния файл: $lastline" break fi

Свършен
Machine Translated by Google

Списък 10.17 започва с инициализиране на някои променливи с твърдо кодирани стойности,


които се отнасят до лог файл, който е указан чрез променливата DataFile, както е показано тук:

DataFile="mylogfile.txt"
OK="добре"
ERROR="грешка"

sleeptime="2" loopcount=0

Следващата част от кода безусловно изтрива файла DataFile към


уверете се, че първоначално е празен всеки път, когато се извиква този shell скрипт.
Основната част от Листинг 10.17 е цикъл while , който съдържа няколко подсекции. Първата
подсекция заспива за $sleeptime секунди и след това проверява последния ред в текстовия файл,
дефиниран от променливата DataFile. Ако последният ред е празен, операторът continue връща
изпълнението в горната част на цикъла while .

В противен случай следващият подраздел инициализира променливите okstatus и badstatus чрез

извикване на командата за намиране на grep


срещания на променливите OK и ERROR , които са
инициализирани съответно със стойностите okay и error. Ако променливата okstatus не е празна,
тогава към лог файла се добавя съобщение в зависимост от стойността на променливата loopcount.

Забележете, че две различни съобщения могат да бъдат добавени към лог файла: ако е добавено
съобщение за грешка, това съобщение ще бъде открито по време на следваща итерация на цикъла
while .
Въпреки това, ако okstatus е празен и променливата badstatus не е празна, тогава към лог файла
се добавя съобщение за грешка и командата break излиза от цикъла while . Примерно извикване на
листинг 10.17 е тук:

спя 2 секунди... отново съм буден...


системата е нормална
спи 2 секунди... отново съм
буден...

Грешка в регистрационния файл: петък, 27 септември, 21:28:53 PDT 2013 СИСТЕМНА ГРЕШКА

Този примерен код наистина е измислен, но можете да използвате същата (или подобна) логика,
в случай че трябва да анализирате съдържанието на регистрационен файл, за да проверите за
съобщения за грешка, след което можете да предоставите някакъв вид актуализация на състоянието.
Machine Translated by Google

ИЗВЕСТЯВАНЕ НА АКТИВНИ ПОТРЕБИТЕЛИ НА МАШИНА

Има различни команди за намиране на информация за потребители на a


Unix система, като finger, who, uname и whoami. Списъци с команди who
информация за различни потребители, както е показано тук:

ocampesato конзола 17 август 08:15


ocampesato ttys000 17 август 08:16
ocampesato ttys001 17 август 08:16

ocampesato ttys011 17 август 08:16
ocampesato ttys012 17 август 08:16

Командата w предоставя допълнителна информация за потребителите, както е показано


тук:

22:30 до 28 дни, 14:18, 14 потребители, средни стойности на натоварване: 1,14 1,33 1,90
ПОТРЕБИТЕЛ TTY ОТ ВХОД@ ПРАЗЕН КАКВО
ocampesato конзола - ocampesato Четвъртък, 05 сутринта 28 дни -
- 10 vi shell-
s011 programming- Четвъртък, 05 сутринта

outline.txt
-
ocampesato s012 ocampesato Четвъртък05 сутринта 2 дни - баш
- Чет 05 сутринта 27:01 - баш
s000 ocampesato s002
- Четвъртък, 05 сутринта, 3:31 vi demo-list.txt
ocampesato s001 ocampesato
-
s004 events.txt Четвъртък05 сутринта 2 дни - баш
- Чет, 05 сутринта 33:52 vi todo-

-
ocampesato s007 Четвъртък05 сутринта 2 дни - баш

Можете също да видите потребителите, които са влезли в Unix машина с


потребителска команда. Ако имате MacBook, вероятно ще сте единственият
човек, влязъл във вашата машина.

РАЗНИ КОМАНДИ

Командата who показва списък с влезли потребители. Ако сте единственият


потребител на вашия MacBook, ще видите нещо като следното:

oswaldcampesato конзола 29 декември 02:18


oswaldcampesato ttys000 5 март 15:03
oswaldcampesato ttys001 22 март 09:30
oswaldcampesato ttys002 24 март 18:28
oswaldcampesato ttys003 25 март 14:03
Machine Translated by Google

Командата df показва количеството свободно дисково пространство и кога вие


извикайте тази команда с опцията -k , изходът изглежда нещо подобно:

Файлова система 1024-блокове Използван наличен капацитет iused


ifree %iused Монтирана на
/dev/disk1 936490368 839326208 96908160 90% 17483073
4277484206 0% /
devfs 0 189 189 0 100% 655
100% /dev
map -hosts 0 0 0 0 100% 0
100% /net
карта auto_home 0 0 0 0 100% 0
100% /начало

Командите man и info предоставят резюме за командите bash . Ан


пример за началната част от изхода от man ls е тук:

LS (1) Ръководство за общи команди на BSD


LS (1)

ИМЕ
ls -- списък на съдържанието на директорията

СИНОПСИС
ls [-ABCFGHLOPRSTUW@abcdefghiklmnopqrstuwx1] [файл...]

ОПИСАНИЕ
За всеки операнд, който назовава файл от тип, различен от
указател,
ls показва своето име, както и всяко поискано свързано
информация-

ция. За всеки операнд, който наименува файл от тип директория,


ls
показва имената на файловете, съдържащи се в тази директория,
като

както и всяка поискана свързана информация.

Командата curl е помощна програма, която чете съдържанието на URL от


командна линия. Например можете да прочетете съдържанието на началната страница на Google
страница и пренасочете съдържанието й към файл със следната команда:

curl https://www.google.com >y1

Командата за заспиване е еквивалент на обвивката на цикъл на изчакване : тя спира за a


определен брой секунди, без да прави нищо, докато е на пауза. Не може да бъде
Machine Translated by Google

полезен за определяне на времето или в процеси, работещи във фонов режим, проверяващи за
конкретно събитие от време на време (изпитване).

сън 4 # Пауза 4 секунди.

ЗАБЕЛЕЖКА Командата за заспиване по подразбиране е секунди, но могат също да бъдат посочени


минути, часове или дни.

сън 4 часа # Паузи 4 часа

Командата su ви позволява да превключите към суперпотребител, докато командата sudo ви


позволява да изпълните конкретна команда като суперпотребител, но без да превключвате към
суперпотребител.
Помощната програма make е основно за компилиране на C или C++ програми, но
можете да използвате тази команда и за други цели. По подразбиране помощната
програма make търси файла Makefile („голям make“), последван от файла makefile
(„small make“). Можете да посочите различен файл чрез превключвателя -f , както е
показано тук:

make -f myfile

Командата tee ви позволява да пренасочите копие на изхода на a


команда към файл, както е показано тук:

ls -l | tee /tmp/файлове

Можете да добавите към съществуващ файл с опцията -a , както е показано тук:

ls -l | tee -a /tmp/файлове

Командата nice променя приоритета на планиране на команда, което


означава, че можете да увеличите или намалите неговия
The синхронизиране приоритет. командата синхронизира съдържанието на вашия твърд диск със
съдържанието на буферите в паметта. Командата finger предоставя свързана с потребителя
информация за конкретен потребител.
Командата cal показва информация, свързана с календара. Изпълнете командата cal без
аргументи, ако искате информация за текущия месец и година и изпълнете cal 2020, ако искате
информация за всички месеци
Machine Translated by Google

през 2020 г. (или някоя друга година). Например, ако текущият месец и година са
февруари 2020 г., командата cal показва следния резултат:

февруари 2020 г
Су По Вт Ср Чет Пет Съб
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15
16 17 18 19 20 21 22

23, 24, 25, 26, 27, 28, 29

Mktemp ви позволява да създавате имена на временни файлове, които са полезни за


съхраняване на междинни резултати по време на изпълнението на дълготрайни скриптове
на обвивката.

РЕЗЮМЕ

В тази глава научихте как избирателно да копирате и изтривате файлове от дърво


на директории. След това видяхте как да създадете поддиректории в директория въз
основа на набор от низове. Един от тези шел скриптове ви показва как да проверите
дали даден низ е съществуваща поддиректория или файл.
Освен това научихте как да работите с файлове, свързани с компресия, като gzip и
gunzip. След това видяхте как да планирате задачи с командите at и crontab , както и
как да прекратите процеси с командата kill . И накрая, научихте набор от различни
команди, като тези, които показват влезли потребители, информация, свързана с
диска, и информация, свързана с календара.

В този момент има още нещо да кажем: Поздравления! Завършили сте бърза, но
плътна книга и ако сте баш неофит, материалът вероятно ще ви занимава много
часове. Примерите в главите предоставят солидна основа, а приложението съдържа
допълнителни примери и случаи на употреба, за да илюстрира допълнително как
командите bash работят заедно. Комбинираният ефект показва, че вселената от
възможности е по-голяма от примерите в тази книга и в крайна сметка те ще
предизвикат идеи във вас. Късмет!
Machine Translated by Google

ИНДЕКС

А
AppendRow.sh, 91–92
Аритметични оператори, 71–
72 команда expr, 70–71 в
обвивки на POSIX, 70
масиви
array-function.sh, 97 array-loops1.sh,
97 в bash, 92–94 fruits-array1.sh ,
95 функция
items(), 97 names.txt и array-from-
file.sh, 96 команда awk
(Aho, Weinberger и Kernighan), 146 вградени променливи,
146 условна логика и контролни изрази, 148–151 броя на
срещанията на низ,
163–164 честота на преброяване на думи от набори
от данни, 170–171 изтриване на

алтернативни редове, 151 редове за


сливане, 151–154
onlywords.sh, 171–173
пренареждане на колони, 165–
168 шаблон за съвпадение,
155 многоредови
записа, 173–174 числови
функции в, 158–160 едноредови
команди, 161 постфикс
аритметични оператора, 157–158
оператор printf(), 148 печатни
реда, 156 печат на
думите в текстов низ, 162–165
RemoveColumn.sh, 168 кратки
awk-базирани скрипта , 161–162
разделени имена на
файлове, 157 sub-rows-cols.txt, 168–170
Machine Translated by Google

случай на използване,

174–176 принцип на работа, 147

Функция за обратна отметка или заместване на команда, 18

Булеви оператори, 70, 75–76


Bourne Again Shell (Bash), 3 масива,
92–94 команда
cd, 4 команда
история, 4–5 команда man, 4
символа за тръба
(„|“), 9–10 команда pwd, 4
привилегии rwx, 11
zsh, 26– 27

Bourne shell,
функция за заместване на 2 команди,
18 подкатегории, 2

Изявление C case/esac, 79–82 cat


команда, 7–8
Класове символи, 25–26
команда chmod, 11
Условна логика, оператори if/else/fi, 78–79
Снаряди тип C, 3
команда за рязане, 16–17

д
Набори от данни

awk команда брои

честота на думите, 170–171 изтриване на


алтернативни редове, 151
сливане на редове, 151–
154 onlywords.sh, 171–173 повторно
подравняване на колони, 165–
168 данни от диапазон от колони, 66–67
обработка на множество, в shell скриптове, 214–
218 команда за поставяне, свързване на последователни
редове, 22 команда sed

класове знаци, 132–133


контролни знака, 133–134 чисти
думи за показване, 136–137
печатни реда, 131–132
WordCountInFile.sh, 134 неравни реда в,
67–68 команда diff и cmp,
56 директории
Machine Translated by Google

абсолютни и относителни, 42–43

команда cd, 44

проверки за файлове, 84–85

команда mkdir, 43 команда

mv (преместване), 45 команда rmdir,

44

Дърво на директории, 42

echo команда и бели интервали, 17–18 egrep команда,


116–119
Псевдоними на променливи

на средата, 14–15

команда env, 13

Настройка на PATH, 13–14


полезно, 13

команда fgrep, 119

Команди за компресиране на файлове


команда bunzip2, 64 команда

cpio, 63–64 команда gzip и

gunzip, 64 команда tar, 62–63 команда zip, 64–


65

Имена на файлове, проблемно боравене, 12

Собственост на файла, 11
Files

basename команда, 39 basename,

dirname и файлови команди, 33 cat команда, 34–35 cmp команда, 38–

39 копиране на файлове, 30–

31 създаване на текстови

файлове, 29–30
изтриване на файлове, 31–32

dirname команда, 39

head команда, 35–36 команда ln,

32–33 команда повече и по-

малко, 35 преместване на

файлове, 32 разрешения команда chmod,

40–41 команди

chown и chgrp,
41 команди umask и ulimit, 41

команда низове, 39 команда tail, 37–38 wc


команда, 33–34

Файлови тестови оператори, 70, 77–78


Machine Translated by Google

намиране на команда, 61–62


Намиране на изпълними
файлове, команда 15 пъти, 10–11,
52–53 for цикъл, 83

команда gawk, 146


команда grep („Отпечатване на глобален регулярен израз“), 100
обратни препратки, 109–111
знак „\“ обратна наклонена черта, 112–
113 с класове на знаци, 106 --
color и превключвател -v, 105 опция
-c, 102–103, 106–107 празни редове
в набори от данни, 111–112 опция -e,
102–103 опция -h и -o,
104 опция -i и -v, 103 опция
-il и -n, 104 -iv и - l опция,
103–104 joinlines.csv, 120
ключа за търсене на набори от
данни, 112 lines.txt, 101–102
съвпадение на диапазон от
редове, 108–109 метазнака,
101 множество съвпадения в, 113
test1.csv, 119 уникална
стойност на ключ, 115–116
опция -w, 105 команда
xargs, 113–115

Главна команда, 8
Скрити файлове, 11–12

аз

Разделител на вътрешно поле (IFS), 65–66

команда за присъединяване, 52

Linux, 2

команда ls (изброяване на имена на файлове), 5–7

Метасимволи, 24–25 и
класове знаци, 47–48 имена на
файлове и 48–49
Machine Translated by Google

nawk команда, 146


Вложен цикъл, 85–86

od command, 56–57
Оператори
аритметични оператори, 71–72
команда expr, 70–71 в
обвивки на POSIX, 70
булеви оператори, 70, 75–76
оператори за тестване на
файлове, 70, 77–78 оператори за низове, 70, 75–76

команда за поставяне, 20
вмъкване на празен ред с, 21–22

свързване на последователни редове в набор


от данни, 22 обратни колони, 23

Символ на линия („|“), 9–10


и множество команди, 19–20 printf
и команда echo, 16

Q
Цитати знаци, видове, 45–46

Команда S sed „редактор на потоци“, 123


обратни препратки в, 135
класове

символи на набори от данни,


132–133 контролни символа, 133–
134 чисти думи за показване, 136–
137 печатни реда, 131–
132 WordCountInFile.sh, 134 цикъл на
изпълнение, 124
съвпадащ низ шаблони, 124–125
множество разделители, 129–
130 едноредови sed команди, 137–143
търсене и замяна с, 128–129 заместващи
низови шаблони, 125–127 полезни
превключвания, 130–131
Точка и запетая („;“) за разделяне на команди, 20
Shellscripts, 180
команда awk, 211–212
команди bc и dc, 240–241 команда
case/esac, 228–230 проверка на
резултатите от изпълнението, 237–238
Machine Translated by Google

CheckLogUpdates.sh, 245–246 функция


checkNewUser(), 191–192 клониране на
поддиректории, 224–227
комбинация от стойности на полета,
218–219 при команда,
233–234 съдържание
на записи, 211 команда
crontab, 232 команда
curl, 248 дата команда,
241–243 команда
dd, 231 команда
df, 247 диагонални и недиагонални
елементи, 212–214 функция
divisors(), 198–200 команда
dmesg, 238–239
команда du, 239 променливи
на средата, 181–183 изпълняващи файлове
в множество директории, 227–228
функция factorial(), 193–
195 функция fib(), 195–196
команда за
намиране, 222–223
функции, 183–
184 функция gcd(), 196–197 команда
grep
CheckLogUpdates.sh, 208–210 for цикъл,
203–204 релационни и
нерелационни данни, 206–208
обработка на множество
набори от данни, 214–218
функция iterate(), 186–189
команда kill, 234–
235 функция lcm(), 197–198 makedirs.sh,
223 –224 помощна
програма make, 248
команди man и info, 247–248
команда mktemp,
249 многоредови
записи, 210–211 ncompress.sh,
232–233 команда nice, 249
команда nohup, 234 предаване
на стойности към
функция, 184–186
позиционни параметри, 190–191
команди, свързани с печата,
243–244 ps команда, 235–236
рекурсия, 192–193
дистанционно изпълнение на команди, 234 rm и mv команда, 221–222 searchstrings.sh, 202–203 skutotals.sh, 204–206 к
Machine Translated by Google

sourcing, 182
su команда, 248 &
switch, 234
sync и cal команда, 249 tee
команда, 248 top
команда, 236 trap
оператор, 239–240 uuencode
програма, 230–231 who
команда, 246–247 zip
файлове съдържат SVG документи, 201 –202
команда за сортиране, 53–
55 команда за разделяне, 53
Потоци и команди за пренасочване, 46–47
Стрингови оператори, 70, 75–76
Низове в shell скриптове, 82

T
tail команда, 8–9 команда
tee, 62 tr команда,
57–60

uniq команда, 55–56 Unix, 2


обвивки,
2–3 срещу Linux,
2 цикъл до,
89 дефинирани от
потребителя функции, 89–91

V
Променливи,

присвояващи стойности на, 73–


74 команда за четене, 74–75

У
команда wc, 7
команда whatis, 15
команда whereis, 15 команда
which, 15 оператори
while, case/esac и if/elif/else/fi, 88–89 цикъл while, 86–88

zip и bz файлове, команди за, 65 zsh bash и,


26–

27 конфигурация, 27

You might also like