Программирование на 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".
НА ВСЯКИЙ СЛУЧАЙ заметим, что на передачу значений
переменных никакого влияния не оказывает "физическое"
взаимное расположение (файлов) расчетов в файловой системе.



