Программирование на shell (UNIX), часть 2



3.3. Перенаправление команд

     Стандартный ввод (вход) - "stdin" в ОС UNIX осуществляется с

клавиатуры  терминала,  а  стандартный  вывод  (выход) - "stdout"

направлен  на  экран терминала. Существует еще и стандартный файл

диагностических  сообщений  - "stderr", о котором речь будет чуть

позже.

     Команда,  которая  может  работать  со  стандартным входом и

выходом, называется ФИЛЬТРОМ.

     Пользователь  имеет удобные средства перенаправления ввода и

вывода   на   другие  файлы  (устройства).  Символы  ">
"  и  ">
>
"

обозначают перенаправление вывода.

	ls >
f1

     команда  "ls"  сформирует  список файлов текущего каталога и

поместит его в файл "f1" (вместо выдачи на экран). Если файл "f1"

до этого существовал, то он будет затерт новым.

     pwd >
>
f1

     команда  pwd  сформирует  полное  имя  текущего  каталога  и

поместит его в конец файла "f1", т.е. ">
>
" добавляет в файл, если

он непустой.

     Символы "
<" и "
<
<" обозначают перенаправление ввода.

     wc -l

< f4   и   wc -l >
f4

 f2

     Данный  конвейер  из  файла "f1" ("cat") выберет все строки,

содержащие   слово   "result"   ("grep"),   отсортирует  ("sort")

полученные  строки,  а  затем  пронумерует  ("cat  -b") и выведет

результат в файл "f2".

     Поскольку  устройства  в  ОС  UNIX представлены специальными

файлами,  их можно использовать при перенаправлениях. Специальные

файлы  находятся  в  каталоге  "/dev".  Например,  "lp" - печать;

"console"  -  консоль; "ttyi" - i-ый терминал; "null" - фиктивный

(пустой) файл (устройство).

     Тогда,  например,

     ls >
 /dev/lp

     выведет  содержимое  текущего каталога на печать, а

     f1
< /dev/null  обнулит  файл "f1".      sort f1 | tee /dev/lp | tail -20
В  этом  случае  будет  отсортирован  файл "f1" и передан на печать,
а 20 последних строк также будут выданы на экран.
Вернемся  к  перенаправлению выхода. Стандартные файлы имеют номера:
0  -  stdin,  1  -  stdout  и  2  -  stderr.
Если вам не желательно  иметь  на  экране  сообщение  об  ошибке,
вы  можете перенаправить  его  с  экрана  в  указанный вами файл
(или вообще "выбросить", перенаправив  в  файл
 "пустого  устройства"  - /dev/null). Например при выполнении команды
 cat f1 f2
 которая  должна  выдать  на экран последовательно содержимое
 файлов "f1" и "f2", выдаст вам, например, следующее
  111111 222222     cat: f2: No such file or directory
где 111111 222222 - содержимое файла "f1", а файл "f2" отсутствует,
 о чем команда "cat" выдала сообщение в стандартный файл диагностики,
 по умолчанию, как и стандартный выход, представленный экраном.
 Если  вам не желательно такое сообщение на экране, его можно
перенаправить в указанный вами файл:
cat f1 f2 2>
f-err

     сообщения  об  ошибках  будут  направляться (об этом говорит

перенаправление  "2>
")  в  файл  "f-err".  Кстати,  вы можете всю

информацию  направлять  в  один  файл  "ff", использовав в данном

случае конструкцию

     cat f1 f2 >
>
ff 2>
ff

     Можно   указать   не  только  какой  из  стандартных  файлов

перенаправлять,   но  и  в  какой  стандартный  файл  осуществить

перенаправление.

     cat f1 f2 2>
>
ff 1>
&2

     Здесь    сначала   "stderr"   перенаправляется   (в   режиме

добавления)    в   файл   "ff",   а   затем   стандартный   выход

перенаправляется  на  "stderr",  которым к этому моменту является

файл "ff". То есть результат будет аналогичен предыдущему.

     Конструкция "1>
&2" - означает, что кроме номера стандартного

файла,  в который перенаправить,  необходимо впереди ставить "&";

вся конструкция пишется без пробелов.

<&-   - закрывает стандартный ввод.      >
&-   - закрывает стандартный вывод.

	  3.4. Генерация имен файлов.

     При генерации имен используют метасимволы:

     *   -   произвольная  (возможно  пустая)  последовательность

символов;

     ? - один произвольный символ;

     [...] - любой из символов, указанных в скобках перечислением

и/или с указанием диапазона;

     cat f* - выдаст все файлы каталога, начинающиеся с "f";

     cat *f* - выдаст все файлы, содержащие "f";

     cat   program.?   -   выдаст   файлы   данного   каталога  с

однобуквенными расширениями, скажем "program.c" и "program.o", но

не выдаст "program.com";

     cat  [a-d]*  -  выдаст файлы, которые начинаются с "a", "b",

"c", "d". Аналогичный эффект дадут и команды "cat [abcd]*" и

"cat [bdac]*".

		  3.5. Командные файлы.

     Для  того,  чтобы текстовый файл можно было использовать как

команду, существует несколько возможностей.

     Пусть  с  помощью  редактора  создан  файл  с  именем "cmd",

содержащий одну строку следующего вида:

     date; pwd; ls

     Можно  вызвать  shell  как команду (!), обозначаемую "sh", и

передать  ей  файл "cmd",  как  аргумент или как перенаправленный

вход, т.е.

      $  sh cmd    или     $ sh

. Введенное вами число

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

символов(!).  Интерпретор  не  проверяет, что вы ввели. Поэтому в

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

абракадабра  или  просто  нажатие

,  как  значение пустой

строки.  (Для обеспечения проверки формата ввода следует написать

свою команду).

     При  обращении  к  shell-переменной  необходимо перед именем

ставить символ "$". Так команды

     echo $var_2

     echo var_2

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

     ОС UNIX

     var_2

     И еще один пример. Фрагмент командного файла:

     echo "var_2 = $var_2"

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

     var_2 = ОС UNIX

     В  команде  "echo" первое использование "var_2" - это просто

текст,   а  второе  ("$var_2")  -  это  значение  соответствующей

переменной.

     То  что здесь присутствуют пробелы между именем переменной и

символом  присваивания,  а  также  между  символом присваивания и

значением,  так  это  потому,  что  здесь  мы  имеем  дело лишь с

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

действительно  выполняется  присваивание,  пробелы  в этих местах

НЕДОПУСТИМЫ.   Присваивание,  скажем,  w=  означает  присваивание

переменной   "w"   пустой   строки.  Но  и  пустую  строку  лучше

присваивать аккуратно, например w="".

     Для  того,  чтобы  имя  переменной  не сливалось со строкой,

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

     Пусть  a=/mnt/lab/asu/

тогда

     cat /mnt/lab/asu/prim

и

     cat ${a}prim

     равноценны  (т.е.  "cat" выдаст на экран содержимое одного и

того же файла).

     Если  также  предположить,  что  в  системе  есть переменная

"prim" и "prim=dir" , то команда

     echo ${a}$prim

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

     /mnt/lab/asu/dir

		  4.2. Экранирование

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

в  shell.  В  качестве средств экранирования используются двойные

кавычки (" "), одинарные кавычки (' ') и бэк-слэш (\).

     Из примеров очевидно их действие:

     Можно  в  одной  строке  записывать  несколько приcваиваний.

     x=22 y=33 z=$x

     A="$x" B='$x' C=\$x

     D="$x + $y + $z"  E='$x + $y + $z'  F=$x\ +\ $y\ +\ $z

     (присваивание G=$x + $y не было бы выполнено из-за пробелов)

     Тогда

     echo A = $A   B = $B   C = $C

     echo D = $D   E = $E   F = $F

     eval echo evaluated A = $A

     eval echo evaluated B = $B

     eval echo evaluated C = $C

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

     A = 22 B = $x C = $x

     D = 22 + 33 + 22 E = $x + $y + $z F = 22 + 33 + 22

     evaluated A = 22

     evaluated B = 22

     evaluated C = 22

     ВНИМАНИЕ. В трех последних случаях использована своеобразная

команда   "eval"   (от   evaluate   -   означивать),   которая  в

подставленной  в  нее  (в качестве  аргумента) команде  выполняет

означивание  переменных  (если  таковые  имеются).  В  результате

значение "A" остается прежним, поскольку "A" имеет значение "22".

А  переменные "B" и "C" имеют значение "$x". За счет означивания,

которое было выполнено командой "eval" - evaluated "B" и "C" дают

значения "22".

     Еще один пример на "eval".

     Пусть

     w=\$v v=\$u u=5

     В результате выполнения команд

	       echo $w

	  eval echo $w

     eval eval echo $w

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

     $v

     $u

     5

     Приведем  еще  примеры,  связанные с экранированием перевода

строки.  Пусть  переменной  "string" присвоено значение "массива"

2x3:

abc

def

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

пробелов  вторая строка массива начата с первой позиции следующей

строки:

string="abc

def"

Тогда три варианта записи переменной в команде "echo"

echo  $string

echo '$string'

echo "$string"

дадут соответсвенно три различных результата:

abc def

$string

abc

def

а последовательность команд

echo "строка первая

строка вторая" >
 f1

echo 'строка первая

строка вторая' >
 f2

cat f1 f2

даст выдаст последовательно одинаковые файлы f1 и f2:

строка первая

строка вторая

строка первая

строка вторая

     Заметим   также,  что  бэк-слэш  (\)  не  только  экранирует

следующий  за  ним символ, что позволяет использовать специальные

символы  просто  как  символы, представляющие сами себя (он может

экранировать  и  сам  себя  -  \\), но в командном файле бэк-слэш

позволяет об'единять строки в одну (экранировать конец строки).

    Например, приводившийся ранее пример командной строки:

     cat f1 | grep -h result | sort | cat -b >
 f2

может быть записан в командном файле, скажем, как

     cat f1 | grep -h  \

     result | sort | cat -b >
 f2

     Кстати,  эффект  продолжения командной строки обеспечивает и

символ   конвейера.   В   данном  случае  это  может  дать  более

симпатичный результат, например:

     cat f1         |

     grep -h result |

     sort           |

     cat -b >
 f2

	       4.3. Манипуляции с shell-переменными

     Несмотря   на   то,  что  shell-переменные  в  общем  случае

воспринимаются  как  строки, т.е "35" - это не число, а строка из

двух   символов   "3"   и   "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

     ВНИМАНИЕ.  Операция  умножения ("*") обязательно должна быть

заэкранирована, поскольку в shell этот значек воспринимается, как

спецсимвол,  означающий,  что на это место может быть подставлена

любая последовательность символов.

     Следует  обратить  также  внимание  на обязательные пробелы,

отделяющие переменные и знаки операций.

     С   командой   "expr"  возможны  не  только  (целочисленные)

арифметические операции, но и строковые:

     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

     И наконец, об условной замене переменныйх.

     Если переменные, скажем "х", "y", "z", не определены, то при

обращении к переменным

     ${x-new} - в качестве значения "x" будет выдано "new",

     ${y=new} - в качестве значения "у" будет присвоено "new",

     ${z?new} - в качестве значения "z" будет выдано "z: new" и

соответствующая процедура прекращается.

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

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

     А  в  следующем  случае наоборот, пусть переменная "v" имеет

какое-то значение, тогда

     ${z+new}  -  в  качестве  значения "z" будет выдано "new", а

если не было присвоено значение, то пустая строка.

		    4.4. Экспорт переменных

     В  ОС  UNIX  существует  понятие процесса. Процесс возникает

тогда,   когда   запускается  на  выполнение  какая-либо  команда

(расчет).

     Например,  при  наборе на клавиатуре "р

" порождается

процесс  расчета  "р".  В  свою очередь "р" может породить другие

процессы. Допустим, что "р" вызывает расчеты "р1" и "р2", которые

последовательно порождают соответсвующие процессы.

     У каждого процесса есть своя среда - множество доступных ему

переменных.  Например,  до  запуска  расчета "р" уже существовала

среда,  в  которой  уже  были  определены некоторые переменные (о

стандартных  переменных  речь пойдет несколько позже). Запуск "р"

порождает  новую  среду; уже в ней будут порождены расчеты "р1" и

"р2".

     Переменные   локальны  в  рамках  процесса,  в  котором  они

объявлены,  т.е.  где  им присвоены значения (описание переменных

отсутсвует  -  они  все  одного  типа).  Для того, чтобы они были

доступны  и  другим порождаемым процессам, надо передать их явным

образом. Для этого используется встроенная команда "export".

     Пример.

     Пусть расчет (командный файл) "p", имеющий вид:

     # расчет p

     echo Расчет p

     varX=0 varY=1

     echo varX=$varX varY=$varY

     export varY

     p1  # вызов расчета p1

     p2  # вызов расчета p2

     echo Снова расчет p: varX=$varX varY=$varY

     вызывает командные файлы "p1" и "p2", имеющие вид:

     # расчет p1

     echo Расчет p1

     echo varX=$varX varY=$varY

     varX=a varY=b

     echo varX=$varX varY=$varY

     export varX

     # расчет p2

     echo Расчет p2

     echo varX=$varX varY=$varY

     varX=A varY=B

     echo varX=$varX varY=$varY

     export varY

     На экран будут выданы следующая информация:

     Расчет p

     varX=0 varY=1

     Расчет p1

     varX= varY=1

     varX=a varY=b

     Расчет p2

     varX= varY=1

     varX=A varY=B

     Снова расчет p: varX=0 varY=1

     Из  примера  видно,  что  значения переменных экспортируются

только  в  вызываемые расчеты (и не передаются "вверх" и "вбок").

Экспортировать переменные можно и командой "set" с флагом "-a".

     НА   ВСЯКИЙ   СЛУЧАЙ   заметим,  что  на  передачу  значений

переменных    никакого    влияния   не   оказывает   "физическое"

взаимное расположение (файлов) расчетов в файловой системе.