Лекция 4

 

Лекция 4. 1

Работа в ОС UNIX. 2

Поиск файлов. 2

Интерпретаторы командной строки. Shell 3

BASH.. 3

Переменные среды.. 4

Шаблоны.. 5

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

Отступление на тему языка С. Прямой ввод/вывод. Дескрипторы файлов. 7

Перенаправление потоков в BASH. Понятие конвейра. 8

Подстановка текста из стандартного потока вывода в командную строку. 10

Различные стандартные обозначения и расширения. 10

Оператор цикла for. 10

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

Оператор цикла while. 12

Командные файлы. Параметры командных файлов. 12

Функции и алиасы.. 13

 


 

Работа в ОС UNIX.

 

Б.В.Кениган, Р.Пайк. UNIX --- универсальная среда программирования.

Финансы и статистика. Москва 92.

 

Джеймс Армстронг (мл.). Секреты UNIX. Диалектика. Киев 96.

 

Сергей Дунаев. UNIX. System V. Release 4.2. Диалог МИФИ, Москва 95г.

 

UNIX. X Window. Motif. ОСновы программирования (части I, II)

АО Аналитик. Москва 94.

 

 

Поиск файлов

Для поиска файлов существует целый ряд программ:

find

ключи:

-name

-iname --- ignore case

-maxdepth n --- n=макс.вложенность поддиректорий (1=только тек.)

-maxdepth 1 --- n=мин.вложенность поддиректорий (1=exceptтолько тек.)

-mount (иногда -xdev) --- не исп.др.файловые системы

-atime n --- последний доступ не более n*24 часов (access)

-ctime n --- последнее изменение статуса не более n*24 часов (change)

-mtime n --- последнее изменение не более n*24 часов (change)

-atime +n --- последний доступ не менее n*24 часов (access)

-ctime +n --- последнее изменение статуса не менее n*24 часов (change)

-mtime +n --- последнее изменение не менее n*24 часов (change)

-inum n --- inode=n

-regex expr --- рег. выр. на весь путь

-iregex expr --- рег. выр. на весь путь; case  не важен

-exec expr; ---  `{}' = найденный файл          команда expr выполняется для каждого найденного файла. Например

find . –name ’*.tmp’ –exec rm ’{}’ \;

 

whereis filename        PATH ищет файл filename в стандартных папках (можно задавать имя без расширения)

locate, updatedb         locate ищет файл в базе данных, обновляемой командой updatedb; по умолчанию ищется имя файла, содержащее заданное имя; можно задавать маску на полное (!) имя файла

which filename          ищет файл filename в списке папок переменной PATH


 

Интерпретаторы командной строки. Shell

 

Семейство Bourne Shell: bsh, bash

C-Shell: csh

Korn Shell: ksh

 

Shell существует для обеспечения ввода команд в режиме работы с командной строкой и для обеспечения выполнения командных файлов. Для запуска командного файла запускается отдельная версия интерпретатора командной строки, который, собственно, и обеспечивает последовательное выполнение строк командного файла. При этом по умолчанию запускает системный Shell (обычно в UNIX он называется sh, в Linux вместо него используется bash). Это поведение можно изменить, записав в первой строке командного файла

#!ShellName

где ShellName – имя интерпретатора командной строки, в котором должен выполняться командный файл (обязательно написание полного имени).

Для понимания работы Shell следует иметь представление о следующих понятиях, которые имеют представление в языке С:

·        Дескрипторы ввода/вывода

·        Потоки ввода/вывода

·        Переменные среды

 

Shell в интерактивном режиме работы может выступать в роли login-shell (если он запускается командой login) или non-login (если он запущен из другого Shell).

 

BASH

Bash является стандартным интерпретатором командной строки, использующимся в ОС Linux.

Если Bash является login-shell, то при его запуске выполняются файлы /etc/profile[, ~/.bashrc] (различные реализации могут иметь неслько разное поведение), если они существуют, и выполняется первый из существующих файлов из следующих:

 

~/.bash_profile

~/.bash_login

~/.profile

Здесь под ~ подразумевается домашняя папка пользователя.

 При завершении его работы выполняется командный файл ~/.bash_logout .

 

При начале работы non-login shell выполняются файлы [/etc/profile,] ~/.bashrc , если они существуют.

Отметим, что указанные командные файлы выполняются в рамках данного Shell.

 

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

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

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

·                Указать полный путь от корневой папки; при этом полное имя задачи должно начинаться с символа `/’

·                Указать относительный путь; при этом имя задачи состоит из имен папок, разделенных символами `/’ (в начале стоит имя папки, расположенной в текущей папке, т.е. символа `/’ в начале имени нет). Отметим, что в каждой папке есть ссылка на текущую папку с именем `.’ и ссылка на папку предыдущего уровня с именем `..’ .

·                Указать неполное имя, состоящее только из, собственно, имени задачи без имен папок. В этом случае задача с данным именем последовательно ищется во всех папках, указанных в переменной среды PATH (папки в этой переменной разделены двоеточиями). Выполняется первая найденная задача. Отметим, что автоматически в текущей папке поиск не производится, поэтому для выполнения задачи из текущей папки следует указывать, например, ее относительное имя: ./Taskname .

 

Переменные среды

В ОС UNIX существуют переменные среды (это свойство каждой программы, а не конкретного Shell; т.е. это свойство заложено в самой ОС UNIX).

В bash переменную среды можно задать простым присваиванием

Var=value

При этом, если переменная не существовала, то она автоматически создается. Уничтожить переменную можно командой unset :

unset Var

В дальнейшем переменную можно использовать в виде

$Var или ${Var}

Существуют различные варианты расширенного использования имен переменных среды. Приведем примеры

${Var:offset}               подстрока переменной Var, начиная с позиции offset (индексация с 0)

${Var:offset:length}   подстрока переменной Var, начиная с позиции offset длиной length

${#Var }                      длина строки переменной Var

${Var#pattern}           если начало строки переменной Var соответствует шаблону pattern, то результатом подстановки является строка Var с отброшенной минимальной начальной частью строки, удовлетворяющей шаблону pattern. Например:

X=file.ext

echo ${X#*.}

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

${Var##pattern}         то же, но отбрасывается максимальная начальная часть строки, если начало строки, удовлетворяющей шаблону pattern.

 

${Var%pattern}, ${Var%%pattern} то же, но сравнение и отбрасывание происходит с конца строки. Например:

X=file.ext

echo ${X%.*}

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

 

${Var/pattern/string}, ${Var//pattern/string}           заменить в строке Var максимальное вхождение, отвечающее шаблону pattern на string. В первом случае – один раз, во втором – все вхождения. Например:

X=’bad file name.c’

echo ${X// /_}

На экран будет выведено bad_ file_name.c

 

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

export Var

export Var=Value

Сделать переменную Var  неэкспортируемой можно с помощью команды

export -n Var

 

С переменами среды из яхыка C можно работать с помощью функций

char *getenv( const char *varname );

int putenv( const char *envstring );

 

В Microsoft C:

main( int argc, char *argv[ ], char *envp[ ] );

В gcc функции getenv/putenv модифицируют значения переменной

extern char **environ;

Данный массив указателей терминируется нулевым указателем.

В Microsoft C функции _getenv/_putenv модифицируют значения переменной

extern char **_environ;

 

Шаблоны

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

В шаблонах используются следующие специальные символы

*             любая последовательность символов (включая пустую последовательность)

?             ровно один любой символ

[…]        один из символов, набранных вместо `’; можно задавать диапазон символов: [a-zA-Z] – соответствует одной букве английского алфавита; символ `~’ в первой позиции обозначает инвертирование: [~0-9] – соответствует одной не цифре.

 

Например:

cp * Dir

Получив эту команду,  Shell подставляет вместо символа `* все имена файлов из текущей папки, кроме скрытых. Имена файлов, при этом, разделяются пробелами. Если таковых файлов не найдено, то шаблон используется без изменений. Далее происходит попытка скопировать все файлы, подставленные вместо символа `*’, в папку Dir. Если Dir не является именем папки и, при этом, подставлен более чем один файл, то выдается сообщение об ошибке. Если среди файлов встретились папки, то они игнорируются с соответствующим сообщением об этом (заметим, что, т.к. папка Dir находится в текущей папке, то и ее имя появится в списке подставленных файлов).

Следующий пример был бы корректен в MS DOS, т.к. шаблоны там обрабатываются непосредственно программами, к которым они адресуются:

mv *.c *.cpp

По логике, аналог данной записи в MS DOS: ren *.c *.cpp

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

 

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

Если символы шаблонов необходимо экранировать, то соответствующую запись можно поместить в одинарные или двойные кавычки, или перед соответствующим символом можно поставить символ `\’.

Например:

 

mv ’*.c’  x.c

данная команда переименовывает файл с именем `*.c в файл с именем `x.c.

 

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

Отметим также, что кавычки объединяют все последовательность символов между ними в один параметр командной строки. Так, например, в следующей последовательности с символов с точки зрения bash содержится 3 слова:

1    2’3 4’   ’5 6’

это следующие слова:

1

23 4

5 6

История команд

Bash дает возможность ходить по списку выполненных команд клавишами вверх/вниз.

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

К уже выполненной команде можно обратиться с помощью команд, начинающихся на `!’. Имеется в виду, что команда !... может стоять в любом месте командной строки.

Команда !!  есть синоним предыдущей команды.

!-n  вызывает команду с номером –n от текущей, т.е. !-1 есть синоним !!.

!-n  вызывает команду с номером n (список команд с номерами выдается командой history).

!string  вызывает последнюю команду, начинающуюся на строку string.

!string  вызывает последнюю команду, содержащую на строку string.

 

Отступление на тему языка С. Прямой ввод/вывод. Дескрипторы файлов

Прямой ввод/вывод в языке С осуществляется набором функций

open

close

read

write

lseek

 

Если в случае буферного ввода/вывода в качестве идентификатора файла использовалась переменная типа FILE*, то здесь будет использоваться целая переменная, называемая дескриптором файла. Отметим, что данный дескриптор не имеет никакого отношения к inode, который часто переводится на русский язык как `дескриптор’.

Функция open имеет одно из следующих описаний

       int open(const char *pathname, int flags);

       int open(const char *pathname, int flags, mode_t mode);

Функция открывает файл и возвращает дескриптор файла. В случае ошибки возвращается –1.

Возможные значения поля flags

O_RDONLY

O_WRONLY 

O_RDWR

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

       O_CREAT

       O_EXCL     может использоваться в качестве семафора

       O_TRUNC

       O_APPEND

       O_NONBLOCK или O_NDELAY

       O_SYNC 

 

Поле mode задает привилегии, присваиваемые файлу.

 

Функции read/write в целом аналогичны функциям fread/fwrite в  случае буферного ввода/вывода. Описание:

int read( int handle, void *buffer, unsigned int count );

int write (int handle, void *buffer, unsigned int count );

 

Наконец:

int _close( int handle );

 

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

int fileno( FILE *stream );

FILE *fdopen( int handle, const char *mode );

Для стандартных потоков stdin/stdout/stderr зарезервированы дескрипторы 0/1/2.

 

 

Перенаправление потоков в BASH. Понятие конвейра

Любой вывод, адресуемый в дескриптор n , может быть перенаправлен в файл с именем FileName:

Command n>FileName

 

По умолчанию, перенаправляется вывод в дескриптор 1, т.е. перенаправляется стандартный поток вывода.

 

Ввод из любого дескриптора n  может быть перенаправлен для ввода из файла с именем FileName:

Command n<FileName

 

По умолчанию, перенаправляется ввод для дескриптора 0, т.е. перенаправляется стандартный поток вывода.

 

Пример:

 

#include <stdio.h>

main()

{char str[256]; FILE *f;

 f= fdopen(4,"r");

 if(f==NULL){printf("Error\n");return -1;}

 while(fgets(str,255,f)!=NULL)printf(“%s”,str);

 fclose(f);

 return 0;

}

 

Данная программа c именем Prog может распечатать содержимое файла с именем FileName, если она вызвана следующим образом:

Prog 4< FileName

 

Отметим, что отчасти данная идеология перенята в ОС MSDOS. В этой системе данная программа работать не будет (функция fdopen вернет NULL), но если вместо дескриптора 4 использовать дескриптор 0 (стандартный поток ввода), то она заработает.

Можно перенаправлять вывод программы в конец существующего файла:

Command n>>FileName

 

Можно потребовать вводить до некоторого ключевого слова:

Command <<KeyWord

Точнее, ввод осуществляется до строки, состоящей из одного слова KeyWord.

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

cat >>OutputFileName <<_END_

LongText

_END_

 

 

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

Prog <<<String

 

Перенаправить одновременно поток ввода и вывода в один файл можно с помощью следующей конструкции:

Prog <>FileName

 

Две программы можно запустить одновременно, склеив поток stdout первой программы с потоком stdin  второй:

Prog1 | Prog2

 

Два потока вывода можно склеить:

n>&m - дескриптор вывода n становится копией m, например

find . -name t.t >ttt 2>&1         – весь вывод переходит в файл ttt

find . -name t.t >ttt 1>&2         – весь вывод идет на экран

find . -name t.t 1>&2 >ttt         – запись 1>&2 игнорируется

find . -name t.t 2>&1 >ttt         – запись 2>&1 игнорируется

 

Отметим, что нельзя так просто перенаправлять потоки в конвейере. Например, нельзя так просто склеить потоки stdout и  stderr  одной программы и передать их на поток stdin  второй программы в конвейере. Но можно запустить первую программу в порожденном Shell. Это делается с помощью заключения команды в круглые скобки:

(Prog1 2>&1) | Prog2

 

Подстановка текста из стандартного потока вывода в командную строку

Если в текст исходной команды вставить некоторую другую команду com2, заключенную в обратные кавычки, то перед выполнением исходной команды будет выполнена команда com2 и текст из ее потока stdout  будет подставлен вместо ее вызова в исходной команде.

Например

echo `ls`

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

Почти эквивалентный синтаксис:

echo $(ls)

Различные стандартные обозначения и расширения

~                        эквивалентно $HOME, т.е. домашняя папка

~+          эквивалентно $PWD , что содержит имя текущей папки

~-           содержит имя предыдущей папки

${var}    подставляется значение переменной var

${#var}  подставляется длина содержимого переменной var

$[expr]   подставляется значение выражения expr, например:

 

echo $[1 + 1]

 команда распечатает на экране:

2

 

Оператор цикла for

Оператор имеет следующий синтаксис:

for var in values

do

commands

done

 

Здесь

var                     имя переменной цикла

values                список значений переменной через пробел

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

 

В командах можно использовать значение переменной цикла в виде $var.

 

Цикл можно набрать и в одной строке:

for var in values ;do commands; done

Если команд несколько, то их можно разделять символом `;’ .

Например:

for i in *; do echo $i; rm –f $i; done

 

Напомним, что ключ f  заставляет команду rm удалять файл без запроса.

 

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

 

for i in *; do mv $i `echo $i |dd conv=lcase`;done

 

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

Оператор имеет следующий синтаксис:

if logical

then

commands

fi

 

Оператор можно набрать и в одной строке:

if logical; then commands; fi

 

Здесь

logical                логическое выражение

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

 

Под `логическим выражением’ понимается команда, код возврата которой служит признаком истины или лжи. Считается, что команда вернула значение истина,  если код возврата процедуры равен 0  (заметим, что это не согласуется с понятием истины в языке С). Любое другое значение ассоциируется с ложью.

Если вышесказанное соотнести с тем, что при успешном выполнении программы принято использовать код возврата = 0, то получим, что имеет смысл следующая команда:

if prog; then echo OK; else echo Failed; done

 

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

 

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

[logical expression]

 

В выражении logical expression допустимо использование строковых операций сравнения:

==

<

>

<=

>=

!=

 

При их использовании необходимо не забывать экранировать их от Shell.

Для численного сравнения используются операторы

-eq

-ne

-lt

-le

-gt

-ge

 

Кроме того, возможны операции, связанные с существованием файлов. Например:

if [-e FileName]; then echo FileName exists; fi

данная команда напечатает FileName exists если файл FileName существует.

 

Оператор цикла while

Оператор имеет следующий синтаксис:

while logical

do

commands

done

 

Оператор можно набрать и в одной строке:

while logical; do commands; done

 

Здесь

logical                логическое выражение (в том же смысле, что и в операторе if )

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

 

Приведем пример команд, создающих файлы с именами 1.tmp, 2.tmp,… , 100.tmp

 

i=0; while [$i –le 100]; do i=`expr $i + 1`; echo ’’ >$i.tmp; done

 

Командные файлы. Параметры командных файлов

Командный файл представляют собой обычный текстовый файл. При его выполнении его строки передаются на вход интерпретатору командной строки и он их последовательно обрабатывает и выполняет.

Обычно командный файл выполняется в отдельно вызывающемся для данyого файла интерпретаторе. Например, если у командного файла установлен атрибут x, то выполнить файл можно просто с помощью написания его имени. При этом автоматически запускается интерпретатор командной строки (обычно его имя берется из переменно среды SHELL, но это может быть и текущий shell) и ему на вход подается командный файл. Задать другой интерпретатор, который должен обрабатывать данный командный файл, можно с помощью написания его имени в первой строке командного файла после символов `#!’:

#!/bin/ash

Командный файл можно вызвать с помощью прямого вызова интерпретатора и передачи ему командного файла в качестве параметра:

bash CommandFile

Кроме того имеется возможность исполнения командного файла в текущем shell с помощью команды `.’ (при этом его синтаксис может быть ограничен):

. CommandFile

 

К параметрам командных файлов можно обращаться по номерам (аналогично MSDOS): $1, $2, …, $9. К имени, с помощью которого был вызван командный файл, можно обратиться через параметр $0.

К списку всех параметров можно обратиться через $*.

Если параметров много, то можно использовать команду shift n, сдвигающую номера параметров так, чтобы параметр с номером n+1 стал бы иметь номер 1. По умолчанию происходит сдвиг на одну позицию (n=1).

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

 

while [”_$1” != ”_”]

do

echo $1

shift

done

 

Функции и алиасы

Определение функции:

function FunName() { commands;}

 

Здесь

FunName           имя функции

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

 

Обращаться к параметрам можно по номерам: $1, $2  и т.д.

Просмотреть список определений всех функций можно с помощью команды

typeset –f

 

Например, часто используемый mc умеет при завершении своей работы менять текущую папку на последнюю текущую папку в самом mc. Ясно, что для любой программы текущая папка является параметром только данной программы и сменить ее можно только в рамках данной программы. Для решения этой проблемы пишется функция с именем mc, которая реально вызывается вместо самого mc. Функция имеет примерно следующий вид:

function mc(){/usr/bin/mc > /tmp/mc.tmp; cd  ”`cat /tmp/mc.tmp `”}

 

Сам mc ответственен за то, чтобы при завершении своей работы выводить имя последней текущей папки в поток stdout.