shell начало:)

Очередная заметочка «чтобы не забыть». Поскольку юникслайк(мои любименькие: фря и мак) оси предоставляют богатый выбор утилит для выполнения различных задач, но они все узконаправленные, если она копает, то она копает!
Для связки всех наших маленьких утилиток нам нужен клей, на эту роль идеально подходил shell: Bourne shell или С shell неважно, но есть одна книга, одного уважаемого автора, в которой сказано, что писать скрипты на Сsh дурной тон, поэтому наш выбор /bin/sh.

/bin/sh это такая же программа на Си которая принимает от Вас команды, и любезно соглашается их выполнять. К слову вы сами можете выбрать произвольный интерпретатор для каждого пользователя, можем даже поставить passwd, В результате пользователь кроме как поменять пароль ничего сделать в системе не сможет:)
Об этом в статье о ssh-туннелировании.

Для запуска шелл скрипта на исполнение нужно установить соответствующий флаг(Х). Запустить его можно 3мя способами
1. Дописать какая оболочка будет обрабатывать этот файл, в нашем случае #!/bin/sh (да, писать с #!)
2. Запуск сценария в текущей оболочке, нужно быть уверенным, что мы писали именно под нее

. programm

В этом случае, указанная программа будет выполнена экземпляром оболочки, в которой Вы сейчас работаете. То есть, для выполнения программы не будет порожден новый процесс. Правда, если в коде программы будет выполнен оператор exit — Вы выйдете из системы.
3. возможен явный вызов интерпретатора в командной строке

sh program.sh

Перенаправление команд:
Стандартный ввод, по умолчанию клавиатура
0 STDIN <

grep init < ps.txt
cat > f2 << F

По последнему примеру небольшое пояснение:
команда cat будет считывать с клавиатуры символы и писать их в файл f2, пока первым и единственным символом не будет F.

Стандартный вывод по умолчанию экран
1 STDOUT > всегда создает заново файл
1 STDOUT >> создает или ДОПИСЫВАЕТ в файл

ls >f1

Консоль ошибок
2 STDERR 2>

ls errfilename 2> ls.txt

«1>&2» — стандартный выход перенаправляется на «stderr»

Конвеер передает вывод одной команды на ввод другой
|

du ­-d1 /var | sort ­-n

Средства группировки:
; определяют последовательное выполнение команд;

pwd; cat tmp.cfg

& фоновое выполнение предшествующей команды;
При выполнении команды в фоновом режиме (после команды стоит один амперсанд) на экран выводится номер процесса, соответствующий выполняемой команде, и система, запустив этот фоновый процесс, вновь выходит на диалог с пользователем.

$ find / -name conf -print 2> /dev/null &
[1] 503
$ pwd
/Users/Username

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

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

    k1 && k2; k3

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

    k1 && {k2; k3}

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

    {k1; k2} &

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

Круглые скобки «()», кроме выполнения функции группировки, выполняют и функцию вызова нового экземпляра интерпретатора shell.
Пусть мы находились в начальном каталоге «/mnt/lab/asu»
Тогда в последовательности команд

    cd ..; ls; ls

две команды «ls» выдадут 2 экземпляра содержимого каталога «/mnt/lab», а последовательность

   (cd ..; ls) ls

выдаст сначала содержимое каталога «/mnt/lab», а затем содержимое «/mnt/lab/asu», т.к. при входе в скобки вызывается новый экземпляр shell, в рамках которого и осуществляется переход. При выходе из круглых скобок происходит возврат в старый shell и в старый каталог.

если мы хотим передать результат выполнения однйо команде другой, то нужно использовать $()
Например:

cd /lib/modules/$(uname -r)/

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

 
var_1=13 - "13" - это не число, а строка из двух цифр.
var_2="ОС UNIX" - здесь двойные кавычки (" ") необходимы, так как в строке есть пробел.
var_3=`date` - сначала выполняется команда "date", а результат записывается в переменную
(обратные кавычки говорят о том, что сначала должна быть выполнена заключенная в них команда)

Для интерактивного присваивания, пользователь сам вводит значение используется read
Можно присваивать много переменных(вводить через пробел)
Если программе echo указать опцию -n, она НЕ добавляет символ перевода строки после вывода данных.

echo -n "Введите трехзначное число:"
read x

Вывод значения переменной используется echo

echo $var_2

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

a='This is a string'
echo "${a}ent test of variables."

В результате получим

This is a stringent test of variables.

Если бы вы не использовали фигурные скобки, то Shell подставил бы пустое значение вместо «$aent» и напечатал:

test of variables.

Сам интерпретатор shell автоматически присваивает значения следующим переменным (параметрам):
? значение, возвращенное последней командой;
$ номер процесса;
! номер фонового процесса;
# число позиционных параметров, передаваемых в shell;
* перечень параметров, как одна строка;
@ перечень параметров, как совокупность слов;
— флаги, передаваемые в shell.

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

Экранирование:
двойные кавычки (» «) — подставляет значения переменных
одинарные кавычки (‘ ‘) — не подставляет значения переменных
бэк-слэш (\) — позволяет использовать специальные символы просто как символы, представляющие сами себя (он может экранировать и сам себя — \\), но в командном файле бэк-слэш позволяет объединять строки в одну (экранировать конец строки).

«eval» (от evaluate — означивать), в подставленной в нее (в качестве аргумента) команде выполняет означивание(подставляет значение) переменных (если таковые имеются).

 w=\$v v=\$u u=5
 echo $w
 eval echo $w
 eval eval echo $w
В результате:
 $v
 $u
 5

expr, интерпретирует значение переменной не как строку, а использует ее значение
Арифметические операции:

    x=7 y=2
    a=`expr $x + $y`   ; echo a=$a
    a=`expr $a + 1`    ; echo a=$a
    b=`expr $y - $x`   ; echo b=$b
    c=`expr $x '*' $y` ; echo c=$c
    d=`expr $x / $y`   ; echo d=$d
    e=`expr $x % $y`   ; echo e=$e

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

    a=9
    a=10
    b=-5
    c=14
    d=3
    e=1

строковые:

    A=`expr 'cocktail' : 'cock'`  ; echo $A
    B=`expr 'cocktail' : 'tail'`  ; echo $B
    C=`expr 'cocktail' : 'cook'`  ; echo $C
    D=`expr 'cock' : 'cocktail'`  ; echo $D

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

    4
    0
    0
    0

Узнать длину строки:
myLen1=${#mystring1}

Если переменные, скажем «х», «y», «z», не определены, то при обращении к переменным
${x-new} в качестве значения «x» будет выдано «new»,
${y=new} в качестве значения «у» будет присвоено «new»,
${z?new} в качестве значения «z» будет выдано «z: new»

Во всех этих случаях, если переменная была к этому времени определена, то ее значение используется обычным образом.

Также можно использовать другой синтаксис для выполнения арифметических операций:
Для подстановки значения арифметических выражений используется:
$(( выражение ))
Если значение переменной, используемой в арифметическом выражении, не является целым числом, то её значение считается=0.

PERM=2
echo "2*2=$(( 2*$PERM ))"
echo "((2*3+5)-4)/2=$(( ((2*3+5)-4)/2 ))"

В результате получим

4
3   (потому что шелл оставляет только целую часть)

Процессы и экспорт:
Каждая программа, работающая в некоторый момент времени, называется процессом. Каждая команда, которую вы запускаете, порождает хотя бы один процесс. Есть несколько системных процессов, запущенных все время и поддерживающих функциональность системы.
У каждого процесса есть уникальный номер, называемый process ID, или PID, и, как и у файлов, у каждого процесса есть владелец и группа.

Для запуска программ в UNIX используются два системных вызова – fork() и exec().
fork() — системный вызов, создающий новый (дочерний процесс, идентичный выполняющему этот вызов. После вызова fork() алгоритм обычно разветвляется (родительский процесс получает от fork() значение PID дочернего процесса, а дочерний получает нуль). После fork() дочерний процесс чаще всего выполняет системный вызов exec().

При завершении работы программа (дочерний процесс) выполняет системный вызов exit(), при этом родительский процесс с помощью системного вызова wait() (или waitpid()) должен очистить таблицы планировщика процессов от информации о своем дочернем процессе. Если этого не происходит и родительский процесс завершается, не вызвав wait() для всех своих дочерних процессов, в системе возникают зомби (zombie), представляющие собой записи в таблицах планировщика процессов. Очистить операционную систему от зомби можно только с помощью перезагрузки.

Переменные локальны в рамках процесса, в котором они объявлены, т.е. где им присвоены значения (описание переменных отсутствует — они все одного типа). Для того, чтобы они были доступны и другим порождаемым процессам, надо передать их явным образом. Для этого используется встроенная команда «export«.

Переменные окружения будут доступны в текущем процессе, а также во всех порожденных этой программой процессах. В других процессах системы эти переменные не будут видны. Для просмотра всех переменных окружения и функций можно воспользоваться командой set. Программа env покажет только переменные, а export — только переменные помеченные как экспортированные. Удаление переменных происходит при помощи оператора unset.
Некоторые, наиболее часто используемые, переменные окружения:
HOME Домашняя директория пользователя.
USER Регистрационное имя пользователя.
PATH Содержит список директорий, разделенных двоеточием, в которых интерпретатор будет искать программу, если пользователь при запуске последней явно не указал путь к ней.
PS1 Внешний вид приглашения командной строки.
PS2 Внешний вид дополнительного приглашения командной строки.
SHELL Содержит интерпретатор по умолчанию текущего пользователя.
TERM Определяет тип терминала.
PWD Содержит текущую директорию.

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

$0 соответствует имени данного командного файла;
$1 первый по порядку параметр;
$2 второй параметр и т.д.
$* все параметры
$# число аргументов

Поскольку число переменных, в которые могут передаваться параметры, ограничено одной цифрой, т.е. 9-ю, то для передачи большего числа параметров используется специальная команда «shift».

Пусть командный файл «many» вызывается с 13-ю параметрами

many 10 20 30 40 50 60 70 80 90 100 110 120 130

И имеет вид

    ###
    # many: Передача большого числа параметров.
    echo "$0: Много параметров"
    echo " Общее число параметров = $#
    Исходное состояние: $1 $5 $9 "
  <strong>  shift</strong>
    echo "1 сдвиг: первый=$1 пятый=$5 девятый=$9"
    shift 2
    echo "1 + 2 = 3 сдвига: первый=$1 пятый=$5 девятый=$9"
    perem=`expr $1 + $2 + $3`
    echo $perem

В результате первого применения команды «shift» второй параметр расчета вызывается как $1, третий параметр вызывается как $2, … десятый параметр, который был исходно недоступен, вызывается как $9. Но стал недоступным первый параметр!

После выполнения этого расчета на экране будет:

    many: Много параметров
    Общее число параметров = 13
    Исходное состояние: 10 50 90
    1 сдвиг: первый=20 пятый=60 девятый=100
    1 + 2 = 3 сдвиг: первый=40 пятый=80 девятый=120
    150
 

Своеобразный подход к параметрам дает команда «set».

Например, фрагмент расчета

    set a b с
    echo первый=$1 второй=$2 третий=$3

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

    первый=a второй=b третий=c

т.е. команда «set» устанавливает значения параметров. Это бывает очень удобно. Например, команда «date» выдает на экран текущую дату, скажем, «Mon May 01 12:15:10 2000», состоящую из пяти слов, тогда

set `date`
echo $1 $3 $5

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

Mon 01 2000

Команда «set» позволяет также осуществлять контроль выполнения программы, например:
set -v Вводимые строки печатаются так, как они считываются Shell’ом. Этот флаг удобен для устранения синтаксических ошибок. Команды на каждой введенной строке выполняются после того, как она будет напечатана.
Set +v отменяет предыдущий режим.
Set -x Команды и их аргументы печатаются по мере их выполнения. (Команды управления Shell, такие как for, while и т.д. не печатаются.) Обратите внимание, что -х вызывает трассировку только тех команд, которые выполняются, а -v печатает всю командную строку, пока не встретится синтаксическая ошибка.
Set +x отменяет предыдущий режим.

Команда «set» без параметров выводит на терминал состояние программной среды.

Test или [ ]
Программа test предназначена для проверки следующих типов условий:
— сравнение различных значений,
— проверка типов и наличия файлов,
— проверка логических условий.
Синтаксис:

 
   test условие
или
    [ условие ]

Программа может проверить два типа логических условий И (AND) и ИЛИ (OR).
● Выражение1 -а Выражение2 — возвращает истину, если истинно и Выражение1 и
Выражение2.
● Выражение1 -о Выражение2 — возвращает истину, если истинно или Выражение1
или Выражение2.
● Оператор ! инвертирует значение логического выражения.
● Сравнение чисел происходит при помощи следующих операторов:
○ число1 -eq число2 — истина, если числа равны.
○ число1 -ne число2 — истина, если числа не равны.
○ число1 -gt число2 — истина, если первое число больше второго.
○ число1 -ge число2 — истина, если первое число больше или равно второму.
○ число1 -lt число2 — истина, если первое число меньше второго.
○ число1 -le число2 — истина, если первое число меньше или равно второму.
● Сравнение строк:
○ -n строка — истина, если строка имеет не нулевую длину.
○ -z строка — истина, если строка имеет нулевую длину
○ строка1 = строка2 — истина, если строка1 идентична строке2.
● Проверка существования и типов файлов:
○ -e /путь/к/файлу — истина, если файл существует.
○ -f /путь/к/файлу — истина, если файл существует и является обыкновенным
файлом.
○ -d /путь/к/файлу — истина, если файл существует и является директорией.
○ -L /путь/к/файлу — истина, если файл существует и является символьной ссылкой.
○ -r /путь/к/файлу — истина, если файл существует и доступен для чтения.
○ -w /путь/к/файлу — истина, если файл существует и доступен на запись.
○ -x /путь/к/файлу — истина, если файл существует и доступен на выполнение.
○ -s /путь/к/файлу — истина, если файл существует и имеет не нулевую длину.


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

Синтаксис:

if условие
 then
  список операторов
[else
список операторов ]
[elif условие
список операторов ]
fi

Пример:

 if [ $1 -gt $2 ]
	then pwd
	else echo $0 : Hello!
 fi

Оператор вызова («case») Оператор выбора «case» имеет структуру:

    case   строка  in
	шаблон)  список команд;;
	шаблон)  список команд;;
	    ...
    esac

Здесь «case» «in» и «esac» — служебные слова. «Строка» (это может быть и один символ) сравнивается с «шаблоном». Затем выполняется «список команд» выбранной строки. Непривычным будет служебное слово «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) ни с одним из ранее записанных шаблонов, ограниченных скобкой «)». Значения просматриваются в порядке записи.
Пример 2:

 # case-2: Реализация меню с помощью команды "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

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

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

где «for» — служебное слово определяющее тип цикла, «do» и «done» — служебные слова, выделяющие тело цикла. Не забывайте про «done»! Фрагмент «in список значений» может отсутствовать. Стоит отметить, что значения после in воспринимаются просто как символы, шелл не воспринимает их значения, вместо 1 2 3 4 5 может быть ф о к у с, результат будет аналогичным:)

    # subdir: Выдает имена всех поддиректорий
    #         директория с именем $dir
	     for i in $dir/*
	     do
		if [ -d  $i ]
		   then echo $i
		fi
	     done

«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-переменными как с целыми числами, а не как со строками.

«break [n]» позволяет выходить из цикла. Если «n» отсутствует, то это эквивалентно «break 1». «n» указывает число вложенных циклов, из которых надо выйти, например, «break 3» — выход из трех вложенных циклов.

«continue [n]» лишь прекращает выполнение текущего цикла и возвращает на НАЧАЛО цикла. Она также может быть с параметром. Например, «continue 2» означает выход на начало второго (если считать из глубины) вложенного цикла.

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

Оператор цикла с ложным условием («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» прекращает выполнение цикла.

Функции в 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», выдаст на экран:

    749
    xx=xx
    xx=xx
    2
    fun: 749 a b
    xx=yy
    5
    xx=yy

getopts — это встроенная в shell команда, позволяющая разобрать командную строку, передаваемую программе. Она понимает только параметры, написанные в стиле POSIX, то есть, параметр должен состоять из одной буквы, перед которой необходимо написать тире. Например: -v, -t, -f file и т.п. При определении параметра символ : означает, что параметр должен иметь дополнительное значение. Пример

#!/bin/sh
while getopts f:o:v OPTION
do
    case $OPTION in
     f) echo "Option f - argument $OPTARG" ;;
     o) echo "Option o - argument $OPTARG" ;;
     v) echo "Option v - no argument" ;;
     \?) echo "Usage: `basename $0` -f infile [-o outfile] [-v]"
    esac
done
exit 0

Программу getopts вызывают как условие цикла while. Она пытается найти аргумент командной строки и, если такой аргумент есть, программа помещает его в переменную OPTION и возвращает истину.
После завершения итерации цикла while, снова вызывается программа getopts. Она ищет следующий аргумент командной строки и если находит, то все повторяется как и в предыдущем случае. Если аргумент не находиться, то программа возвращает ложь и мы выходим из цикла while.
Если у аргумента командной строки присутствует дополнительный параметр, getopts помещает этот параметр в специальную переменную OPTARG. Во время выполнения программы getopts могут возникать следующие ошибки:
• Указана не определенная в параметрах программы опция.
• У аргумента командной строки не указан обязательный дополнительный параметр. Если при работе программы возникает ошибка, она выводит сообщение об ошибке на stderr, а в переменную OPTION помещает символ ?.


Обработка прерываний («trap»)

Бывает необходимо защитить выполнение программы от прерывания.
Наиболее часто приходится встречаться со следующими прерываниями, соответствующими сигналам:
0 выход из интерпретатора,
1 отбой (отключение удаленного абонента),
2 прерывание от ,
9 уничтожение (не перехватывается),
15 окончание выполнения.

Для защиты от прерываний существует команда «trap», имеющая формат:

    trap 'список команд' сигналы

Если в системе возникнут прерывания, чьи сигналы перечислены через пробел в «сигналы», то будет выполнен «список команд», после чего (если в списке команд не была выполнена команда «exit») управление вернется в точку прерывания и продолжится выполнение командного файла.

Например, если перед прекращением по прерываниям выполнения какого то командного файла необходимо удалить файлы в «/tmp», то это может быть выполнено командой «trap»:

   trap 'rm /tmp/* ; exit 1' 1 2 15

которая предшествует прочим командам файла. Здесь, после удаления файлов будет осуществлен выход «exit» из командного файла.

Команда «trap» позволяет и просто игнорировать прерывания, если «список команд» пустой. Так например, если команда «cmd» выполняется очень долго, а пользователь решил отключиться от системы, то для продолжения выполнения этой команды можно написать, запустив команду в фоновом режиме:

( trap » 1; cmd )&

Комментарии: