double arrow

Основные теоретические сведения


Командный язык shell (в переводе - раковина, скорлупа) фактически есть язык программирования очень высокого уровня. На этом языке пользователь осуществляет управление компьютером. Обычно, после входа в систему вы начинаете взаимодействовать с командной оболочкой (если угодно - она начинает взаимодействовать с вами). Признаком того, что оболочка (shell) готова к приему команд служит выдаваемый ею на экран промптер. В простейшем случае это один доллар ("$").

1.1. Общий синтаксис скрипта

Программа на языке shell называется скриптом (script) и представляет собой обычный текстовый файл, в котором записаны инструкции, понятные командному интерпретатору. Это могут быть команды, выражения shell или функции. Командный интерпретатор считывает эти инструкции из файла и последовательно выполняет их.

Созданный скрипт должен иметь право на исполнения, т.е. необходимо выполнить установку этого права:

chmod a+x <имя скрипта>

Безусловно, как и в случае любого другого языка программирования, применение комментариев существенно облегчает последующее использование и модификацию написанной программы. В Bourne shell комментарии начинаются с символа '#':

# Этот скрипт выполняет поиск "мусора" (забытых временных

# файлов, файлов core и т.п.) в каталогах пользователей.

Комментарии могут занимать не всю строку, а следовать после команды:

find /home -name core -print # Выполним поиск файлов core

Поскольку в системе могут существовать скрипты для различных интерпретаторов, имя интерпретирующей команды обычно помещается в первой строке следующим образом:

#!/bin/sh

Команды в shell обычно имеют следующий формат:

<имя команды> <флаги> <аргумент(ы)>

Например:

ls -ls /usr/bin

ls имя команды выдачи содержимого директории /usr/bin,

-ls флаги ( "-" - признак флагов, l - длинный формат, s - об'ем файлов в блоках). Эта команда выдаст на экран в длинном формате содержимое директория /usr/bin, при этом добавит информацию о размере каждого файла в блоках.

grep имя группы /etc/passwd > p1

cat p1

cut -f1,5 -d: p1 > p2

cat p2

sort +1 -t: p2 -o p3

cat p3

1.2. Группировка команд.

Средства группировки:

; и <перевод строки> определяют последовательное выполнение команд;

& асинхронное (фоновое) выполнение предшествующей команды;

&& выполнение последующей команды при условии нормального завершения

предыдущей, иначе игнорировать;

|| выполнение последующей команды при ненормальном завершении предыдущей,

иначе игнорировать.

Для группировки команд также могут использоваться фигурные "{}" и круглые "()" скобки. Рассмотрим примеры, сочетающие различные способы группировки: Если введена командная строка

k1 && k2; k3

где k1, k2 и k3 - какие-то команды, то "k2" будет выполнена только при успешном завершении "k1"; после любого из исходов обработки "k2" (т.е. "k2" будет выполнена, либо пропущена) будет выполнена "k3".

k1 && {k2; k3}

Здесь обе команды ("k2" и "k3") будут выполнены только при успешном завершении "k1".

{k1; k2} &

В фоновом режиме будет выполняться последовательность команд "k1" и "k2".

1.3. SHELL-переменные

Имя shell-переменной - это начинающаяся с буквы последовательность букв, цифр и подчеркиваний. Значение shell-переменной - строка символов. Для присваивания значений переменным может использоваться оператор присваивания "=".

var_1=13 - "13" - это не число, а строка из двух цифр.

var_2="ОС UNIX" - здесь двойные кавычки (" ") необходимы, так как в строке есть пробел. При обращении к shell-переменной необходимо перед именем ставить символ "$". Так команды

echo $var_2

echo var_2

выдадут на экран

ОС UNIX

var_2

1.4. Параметры

В командный файл могут быть переданы параметры. В shell используются позиционные параметры (т.е. существенна очередность их следования). В командном файле соответствующие параметрам переменные (аналогично shell-переменным) начинаются с символа "$", а далее следует одна из цифр от 0 до 9: При обращении к параметрам перед цифрой ставится символ доллара "$" (как и при обращении к переменным): $0 соответствует имени данного командного файла; $1 первый по порядку параметр; $2 второй параметр и т.д. Пусть командный файл с именем "examp-1" имеет содержимое:

echo Это расчет $0:

sort $2 >> $1

cat $1

а файлы "cock" и "tail" содержат соответственно

cock:

Это отсортированный файл:

tail:

Тогда после вызова команды

examp-1 cock tail

на экране будет

Это расчет examp-1:

Это отсортированный файл:

Сам интерпретатор shell автоматически присваивает значения следующим переменным (параметрам):

? значение, возвращенное последней командой;

$ номер процесса;

! номер фонового процесса;

# число позиционных параметров, передаваемых в shell;

* перечень параметров, как одна строка;

@ перечень параметров, как совокупность слов;

- флаги, передаваемые в shell.

При обращении к этим переменным (т.е при использовании их в командном файле - shell-программе) следует впереди ставить "$".

Различия $* и $@ состоит в том, что первая переменная может быть представлена как

"par1 par2 par3"

а вторая как

"par1" "par2" "par3"

1.5. Операторы циклов

1.5.2. Условный оператор "if"

В общем случае оператор "if" имеет структуру

if условие

then список

[elif условие

then список]

[else список]

fi

Здесь "elif" сокращенный вариант от "else if" может быть использован наряду с полным, т.е. допускается вложение произвольного числа операторов "if" (как и других операторов). Конструкции

[elif условие

then список]

и

[else список]

не являются обязательными (в данном случае для указания на необязательность конструкций использованы квадратные скобки - не путать с квадратными скобками команды "test"!). Самая усеченная структура этого оператора

if условие

then список

fi

если выполнено условие (как правило это ком получен код завершения "0", то выполняется "список", иначе он пропускается. Обратите внимание, что структура обязательно завершается служебным словом "fi". Число "fi", естественно, всегда должно соответствовать числу "if".

Примеры.

Пусть написан расчет "if-1"

if [ $1 -gt $2 ]

then pwd

else echo $0 : Hello!

fi

Тогда вызов расчета

if-1 12 11

даст

/home/shal/STUDY/SHELL

а

if-1 12 13

даст

if-1 : Hello!

Возможно использовать в условии то свойство shell, что команды могут выдавать различный код завершения.

Пусть расчет "if-2" будет

if a=`expr "$1" : "$2"`

then echo then a=$a code=$?

else echo else a=$a code=$?

fi

тогда вызов " $if-2 by by " даст then a=2 code=0

а

" $if-2 by be " даст else a=0 code=1

Еще пример на вложенность

###

# if-3: Оценка достижений

echo -n " А какую оценку получил на экзамене?: "

read z

if [ $z = 5 ]

then echo Молодец !

elif [ $z = 4 ]

then echo Все равно молодец !

elif [ $z = 3 ]

then echo Все равно !

elif [ $z = 2 ]

then echo Все !

else echo !

fi

Можно обратить внимание на то, что желательно использовать сдвиги при записи программ, чтобы лучше выделить вложенность структур.

1.5.3. Оператор вызова ("case")

Оператор выбора "case" имеет структуру:

case строка in

шаблон) список команд;;

шаблон) список команд;;

...

esac

Здесь "case" "in" и "esac" - служебные слова. "Строка" (это может быть и один символ) сравнивается с "шаблоном". Затем выполняется "список команд" выбранной строки.

Примеры.

###

# case-1: Структура "case".

# Уже рассматривавшийся в связи со

# структурой "if" пример проще и

# нагляднее можно реализовать с

# помощью структуры "case".

echo -n " А какую оценку получил на экзамене?: "

read z

case $z in

5) echo Молодец ! ;;

4) echo Все равно молодец ! ;;

3) echo Все равно ! ;;

2) echo Все ! ;;

*) echo ! ;;

esac

Обязательно ставить в конце строк выбора ";;"

Обычно последняя строка выбора имеет шаблон "*", что в структуре "case" означает "любое значение". Эта строка выбирается, если не произошло совпадение значения переменной (здесь $z) ни с одним из ранее записанных шаблонов, ограниченных скобкой ")". Значения просматриваются в порядке записи.

###

# case-2: Справочник.

# Для различных фирм по имени выдается

# название холдинга, в который она входит

case $1 in

ONE|TWO|THREE) echo Холдинг: ZERO ;;

MMM|WWW) echo Холдинг: Not-Net ;;

Hi|Hello|Howdoing) echo Холдинг: Привет! ;;

*) echo Нет такой фирмы ;;

esac

При вызове "case-2 Hello" на экран будет выведено:

Холдинг: Привет!

А при вызове "case-2 HELLO" на экран будет выведено:

Нет такой фирмы

Можно эту структуру использовать для реализации простейших меню.

###

# case-3: Реализация меню с помощью команды "case"

echo "Назовите файл, а затем (через пробел)

наберите цифру, соответствующую требуемой

обработке:

1 - отсортировать

2 - выдать на экран

3 - определить число строк "

read x y # x - имя файла, y - что сделать

case $y in

1) sort < $x ;;

2) cat < $x ;;

3) wc -l < $x ;;

*) echo "

Мы не знаем

такой команды ! " ;;

esac

Напишем команду "case-4", которая добавляет информацию к файлу, указанного первым параметром (если параметр один), со стандартного входа, либо (если 2 параметра) из файла, указанного в качестве первого параметра:

###

# case-4: Добавление в файл.

# Использование стандартной переменной.

# "$#" - число параметров при вводе расчета

# ">>" - перенаправление с добавлением в файл

case $# in

1) cat >> $1 ;;

2) cat >> $2 < $1 ;;

*) echo "Формат: case-4 [откуда] куда" ;;

esac

"$1" (при "$#=1") - это имя файла, в который происходит добавление со стандартного входа.

"$1" и "$2" (при $#=2) - это имена файлов , из которого ("$1") и в который ("$2") добавлять.

Во всех других случаях (*) выдается сообщение о том, каким должен быть правильный формат команды.

5.4. Оператор цикла с перечислением ("for")

Оператор цикла "for" имеет структуру:

for имя [in список значений]

do

список команд

done

где "for" - служебное слово определяющее тип цикла, "do" и "done" - служебные слова, выделяющие тело цикла. Фрагмент "in список значений" может отсутствовать.

Пусть команда "lsort" представлена командным файлом

for i in f1 f2 f3

do

proc-sort $i

done

В этом примере имя "i" играет роль параметра цикла. Это имя можно рассматривать как shell-переменную, которой последовательно присваиваются перечисленные значения (i=f1, i=f2, i=f3), и выполняется в цикле команда "proc-sort".

Часто используется форма "for i in *", означающая "для всех файлов текущего каталога".

Пусть "proc-sort" в свою очередь представляется командным файлом

cat $1 | sort > ${1}_sorted

т.е. последовательно сортируются указанные файлы, результаты сортировки направляются в файлы f1_sorted f2_sorted и f3_sorted

Можно сделать более универсальной команду "lsort", если не фиксировать перечень файлов в команде, а передавать произвольное их число параметрами.

Тогда головная программа будет следующей:

for i

do

proc-sort $i

done

Здесь отсутствие после "i" служебного слова "in" с перечислением имен говорит о том , что список поступает через параметры команды. Результат предыдущего примера можно получить, набрав

lsort f1 f2 f3

Усложним ранее рассматривавшуюся задачу (под именем "case-2") определения холдинга фирмы. Теперь можно при вызове указывать произвольное количество фирм. При отсутствии в структуре оператора "for" фрагмента "in список значений", значения берутся из параметров вызывающей команды.

# holding: Справочник.

# Для различных фирм по имени выдается

# название холдинга, в который она входит

for i

do

case $i in

ONE|TWO|THREE) echo Холдинг: ZERO ;;

MMM|WWW) echo Холдинг: Not-Net ;;

Hi|Hello|Howdoing) echo Холдинг: Привет! ;;

*) echo Нет такой фирмы ;;

esac

done

При вызове "holding Hello HELLO ONE" на экране будет:

Холдинг: Привет!

Нет такой фирмы

Холдинг: Not-Net

Еще пример.

# subdir: Выдает имена всех поддиректориев

# директория с именем $dir

for i in $dir/*

do

if [ -d $i ]

then echo $i

fi

done

Отметим различие в специальных переменных "$*" и "$@", представляющих перечень параметров. Первый представляет параметры, как строку, а второй, как совокупность слов.

Пусть командный файл "cmp" имеет вид:

for i in "$*"

do

echo $i

done

echo

for i in "$@"

do

echo $i

done

При вызове: cmp aa bb cc

на экран будет выведено

aa bb cc

aa

bb

cc

5.5. Оператор цикла с истинным условием ("while")

Структура "while", также обеспечивающая выполнение расчетов, предпочтительнее тогда, когда неизвестен заранее точный список значений параметров или этот список должен быть получен в результате вычислений в цикле.

Оператор цикла "while" имеет структуру:

while условие

do

список команд

done

где "while" - служебное слово определяющее тип цикла с истинным условием. Список команд в теле цикла (между "do" и "done") повторяется до тех пор, пока сохраняется истинность условия (т.е. код завершения последней команды в теле цикла равен "0") или цикл не будет прерван изнутри специальными командами ("break", "continue" или "exit"). При первом входе в цикл условие должно выполняться.

###

# print-50: Структура "while"

# Расчет позволяет напечатать 50

# экземпляров файла "file-22"

n=0

while [ $n -lt 50 ] # пока n < 50

do

n=`expr $n + 1`

cat file-22 > /dev/lp

done

Обратим внимание на то, что переменной "n" вначале присваивается значение 0, а не пустая строка, так как команда "expr" работает с shell-переменными как с целыми числами, а не как со строками.

n=`expr $n + 1`

т.е. при каждом выполнении значение "n" увеличивается на 1.

Как и вообще в жизни, можно реализовать то же самое и сложнее. Расчет "рr-br" приведен для иллюстрации бесконечного цикла и использования команды "break", которая обеспечивает прекращение цикла.

###

# рr-br: Структура "while" c "break"

# Расчет позволяет напечатать 50

# экземпляров файла "file-22"

n=0

while true

do

if [ $n -lt 50 ] # если n < 50

then n=`expr $n + 1`

else break

fi

cat file-22 > /dev/lp

done

Команда "break [n]" позволяет выходить из цикла. Если "n" отсутствует, то это эквивалентно "break 1". "n" указывает число вложенных циклов, из которых надо выйти, например, "break 3" - выход из трех вложенных циклов. В отличие от команды "break" команда "continue [n]" лишь прекращает выполнение текущего цикла и возвращает на НАЧАЛО цикла. Она также может быть с параметром. Например, "continue 2" означает выход на начало второго (если считать из глубины) вложенного цикла.

Команда "exit [n]" позволяет выйти вообще из процедуры с кодом возврата "0" или "n" (если параметр "n" указан). Эта команда может использоваться не только в циклах. Даже в линейной последовательности команд она может быть полезна при отладке, чтобы прекратит выполнение (текущего) расчета в заданной точке.

5.6. Оператор цикла с ложным условием ("until")

Оператор цикла "until" имеет структуру:

until условие

do

список команд

done

где "until" - служебное слово определяющее тип цикла с ложным условием. Список команд в теле цикла (между "do" и "done") повторяется до тех пор, пока сохраняется ложность условия или цикл не будет прерван изнутри специальными командами ("break", "continue" или "exit"). При первом входе в цикл условие не должно выполняться.

Отличие от оператора "while" состоит в том, что условие цикла проверяется на ложность (на ненулевой код завершения последней команды тела цикла)проверяется ПОСЛЕ каждого (в том числе и первого!) выполнения команд тела цикла.

Примеры.

until false

do

read x

if [ $x = 5 ]

then echo enough ; break

else echo some more

fi

done

Здесь программа с бесконечным циклом ждет ввода слов (повторяя на экране фразу "some more"), пока не будет введено "5". После этого выдается "enough" и команда "break" прекращает выполнение цикла.

Другой пример ("Ожидание полдня") иллюстрирует возможность использовать в условии вычисления.

until date | grep 12:00:

do

sleep 30

done

Здесь каждые 30 секунд выполняется командная строка условия. Команда "date" выдает текущую дату и время. Команда "grep" получает эту информацию через конвейер и пытается совместить заданный шаблон "12:00:" с временем, выдаваемым командой "date". При несовпадении "grep" выдает код возврата "1", что соответствует значению "ложь", и цикл "выполняет ожидание" в течение 30 секунд, после чего повторяется выполнение условия. В полдень (возможно с несколькими секундами) произойдет сравнение, условие станет истинным, "grep" выдаст на экран соответствующую строку и работа цикла закончится.

5.7. Функции в shell

Функция позволяет подготовить список команд shell для последующего выполнения.

Описание функции имеет вид:

имя()

{

список команд

}

после чего обращение к функции происходит по имени. При выполнении функции не создается нового процесса. Она выполняется в среде соответствующего процесса. Аргументы функции становятся ее позиционными параметрами; имя функции - ее нулевой параметр. Прервать выполнение функции можно оператором "return [n]", где (необязательное) "n" - код возврата.

Пример. Вызов на выполнение файла "fun"

echo $$

fn() # описание функции

{

echo xx=$xx

echo $#

echo $0: $$ $1 $2

xx=yy ; echo xx=$xx

return 5

}

xx=xx ; echo xx=$xx

fn a b # вызов функции "fn" с параметрами

echo $?

echo xx=$xx

содержащего описание и вызов функции "fn", выдаст на экран:

xx=xx

xx=xx

fun: 749 a b

xx=yy

xx=yy

Задание

    1. Выполнить приведённые примеры скриптов. Сравить полученные результаты с ожидаемыми.
    2. Написать командный сценарий (скрипт), реализующий операции в соответствии с вариантом. Запустить сценарий с различными параметрами, сравнить полученные результаты с ожидаемыми.

Варианты заданий.

1. В качестве параметра скрипта указывается список имен файлов. разделенных пробелами. Для каждого из файлов: если файл существует, вывести данные о размере, владельце, правах доступа и т.п.; если не существует – выдать соответствующее сообщение.

2. Написать сценарий, который в зависимости от первого параметра командной строки выдает:

а) общее количество зарегистрированных в системе пользователей;

б) количество работающих в данный момент в системе пользователей;

в) при запуске без параметров или с неверными параметрами – сообщение об ошибке.

Примечание. Общим числом зарегистрированных пользователей считать число строк в файле /etc/passwd.

3. Написать сценарий, определяющий, сколько сеансов указанного пользователя открыто в системе. Если указанный пользователь не присутствует в системе, выдать соответствующее сообщение. Имя пользователя передается в качестве параметра.

4. В качестве параметра скрипта указывается имя файла. Если в файле не более 10 строк, вывести число символов в нем, иначе – число строк.

5. В качестве параметра скрипта указывается имя файла и ключевое слово. Если ключевое слово присутствует в файле, вывести «Есть», иначе – «Нет».

6. В качестве параметров скрипта указывается список пользователей (через пробел). Вывести информацию о сеансах тех из них, кто в данный момент работает в системе.

7. Написать скрипт, который печатает количество указанных параметров командной строки. Если не указано ни одного параметра, вывести сообщение об ошибке.

8. В качестве параметра скрипта указывается имя пользователя. Если протзователь с указанным именем существует, вывести информацию о том, работает ли он в данный момент в системе; иначе – вывести сообщение «Нет такого пользователя»

Примечание. Имена всех существующих (зарегистрированных) пользователей присутствуют в файле /etc/passwd.

9. Если скрипт запущен без параметров, вывести список активных процессов на экран, иначе – в файл, имя которого указано как параметр.

10. Определить, сколько сеансов провел в системе пользователь, имя которого указано в качестве параметра.

Примечание. Информацию о сеансах выдает команда last.

Содержание отчета:

- цель работы;

- результаты выполнения примеров скриптов;

- номер варианта;

- задание для варианта;

- текст командного сценария;

- выводы.



Сейчас читают про: