355 500 произведений, 25 200 авторов.

Электронная библиотека книг » Андрей Робачевский » Операционная система UNIX » Текст книги (страница 7)
Операционная система UNIX
  • Текст добавлен: 6 октября 2016, 02:43

Текст книги "Операционная система UNIX"


Автор книги: Андрей Робачевский


Жанр:

   

ОС и Сети


сообщить о нарушении

Текущая страница: 7 (всего у книги 39 страниц)

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

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

1. Анализирует синтаксис команды. В случае, если обнаружена синтаксическая ошибка, выводится соответствующее сообщение. Естественно, shell анализирует командную строку в соответствии с синтаксисом собственного языка, а не семантику вызова конкретной команды, например, наличие тех или иных аргументов.

2. Производит подстановки, а именно:

 • Заменяет все указанные переменные их значениями. Например, если значение переменной var равно /usr/bin, то при вызове команды find $var -name sh -print переменная $var будет заменена ее значением. Другими словами, фактический запуск команды будет иметь вид:

find /usr/bin -name sh -print

 • Формирует списки файлов, заменяя шаблоны. При этом производится подстановка следующих шаблонов:

  * – соответствует любому имени файла (или его части), кроме начинающихся с символа '.',

  [abc] – соответствует любому символу из перечисленных (а или b или с),

  ? – соответствует любому одиночному символу.

3. Делает соответствующие назначения потоков ввода/вывода. Если в строке присутствуют символы перенаправления (>, <, >>, <<, |), shell производит соответствующее перенаправление потоков. Программный интерфейс ввода/вывода мы рассмотрим в разделе "Работа с файлами" следующей главы.

4. Выполняет команду, передавая ей аргументы с выполненными подстановками. При этом:

 • Если команда является функцией, определенной пользователем, вызывается функция.

 • В противном случае, если команда является встроенной командой shell, запускается встроенная команда.

 • В противном случае производится поиск программы в каталогах, указанных переменной $PATH, если имя команды задано без пути. Если имя команды задано явно, т.е. содержит элементы пути (относительный или абсолютный путь), производится запуск программы. В случае, если программа не найдена, выводится сообщение об ошибке.

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

$ ls                   Вывести список файлов каталога

a.out client client.с

server server.с shmem.h

$ rm *                 Удалить файлы

$ ls

$                      Каталог пуст

Команда rm(1) без колебаний выполнит свою функцию, поскольку в качестве аргументов она получит обычный список файлов. Замену символа '*' на список всех файлов каталога произведет shell, и rm(1) трудно догадаться, что вы собираетесь удалить все файлы. Реальный же вызов rm(1) будет иметь вид:

rm a.out client client.с server server.с shmem.h

Точно так же запускаемые программы ничего не знают о перенаправлении потоков ввода/вывода, произведенных командным интерпретатором. Напомним, что перенаправление ввода/вывода возможно лишь для стандартных потоков ввода, вывода и сообщений об ошибках. Впрочем, большинство утилит UNIX используют только стандартные потоки.

Запуск команд

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

Если необходимо запустить сразу несколько команд, это можно сделать в одной строке, разделив команды символом ';'. Например:

$ pwd; date

Apr 18 1997 21:07

Заметим, что команды будут выполнены последовательно: сначала выполнится команда pwd(1), которая выведет имя текущего каталога, а затем date(1), которая покажет дату и время.

Можно запустить программу в фоновом режиме. В этом случае shell не будет ожидать завершения выполнения программы, а сразу выведет приглашение, и вы сможете продолжить работу в командном интерпретаторе. Для этого строку команды необходимо завершить символом '&':

$ find -name myfile.txt.1 -print >/tmp/myfile.list 2>/dev/null &

$

Пока утилита find(1) производит поиск файла с именем myfile.txt.1, сканируя файловую систему, вы сможете выполнить еще массу полезных дел, например, отправить почту или распечатать документ на принтере. Мы вернемся к этой схеме запуска программ далее в этой главе при обсуждении системы управления заданиями.

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

cmd1 && cmd2

В качестве примера рассмотрим поиск имени пользователя в файле паролей, и в случае успеха – поиск его имени в файле групп:

$ grep sergey /etc/passwd && grep sergey /etc/group

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

Можно назначить выполнение команды только в случае неудачного завершения предыдущей. Для этого команды следует разделить двумя символами '|':

$ cmd1 || echo Команда завершилась неудачно

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

Условные выражения

Язык Bourne shell позволяет осуществлять ветвление программы, предоставляя оператор if. Приведем синтаксис этого оператора:

if условие

then

 command1

 command2

 ...

fi

Команды command1, command2 и т.д. будут выполнены, если истинно условие. Условие может генерироваться одной или несколькими командами. По существу, ложность или истинность условия определяется кодом возврата последней выполненной команды. Например:

if grep sergey /etc/passwd >/dev/null 2>&1

then

 echo пользователь sergey найден в файле паролей

fi

Если слово sergey будет найдено программой grep(1) в файле паролей (код возврата grep(1) равен 0), то будет выведено соответствующее сообщение.

Возможны более сложные формы оператора if.

set `who -r`

Установим позиционные параметры равными значениям полей вывода программы who(1)

if [ «$9» = "S" ]

Девятое поле вывода – предыдущий уровень выполнения системы; символ 'S' означает однопользовательский режим

then

 echo Система загружается

elif [ «$7» = "2" ]

Седьмое поле – текущий уровень

 echo Переход на уровень выполнения 2

else

 echo Переход на уровень выполнения 3

fi

Данный фрагмент скрипта проверяет уровень выполнения, с которого система совершила переход, и текущий уровень выполнения системы. Соответствующие сообщения выводятся на консоль администратора. В этом фрагменте условие генерируется командой test, эквивалентной (и более наглядной) формой которой является «[]». Команда test является наиболее распространенным способом генерации условия для оператора if.

Команда test

Команда test имеет следующий синтаксис:

test выражение

или

[ выражение ]

Команда вычисляет логическое выражение (табл. 1.10) и возвращает 0, если выражение истинно, и 1 в противном случае.

Таблица 1.10. Выражения, используемые в команде test


-s file Размер файла file больше 0
-r file Для файла file разрешен доступ на чтение
-w file Для файла file разрешен доступ на запись
-x file Для файла file разрешено выполнение
-f file Файл file существует и является обычным файлом
-d file Файл file является каталогом
file Файл file является специальным файлом символьного устройства
-b file Файл file является специальным файлом блочного устройства
file Файл file является поименованным каналом
-u file Файл file имеет установленный флаг SUID
-g file Файл file имеет установленный флаг SGID
-k file Файл file имеет установленный флаг sticky bit
-z string Строка string имеет нулевую длину
-n string Длина строки string больше 0
string1 = string2 Две строки идентичны
string1 != string2 Две строки различны
i1 -eq i2 i1 равно i2
i1 -ne i2 i1 не равно i2
i1 -lt i2 i1 строго меньше i2
i1 -le i2 i1 меньше или равно i2
i1 -gt i2 i1 строго больше i2
i1 -ge i2 i1 больше или равно i2

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


!выражение Истинно, если выражение ложно (оператор NOT)
выражение1выражение2 Истинно, если оба выражения истинны (оператор AND)
выражение1 -o выражение2 Истинно, если хотя бы одно из выражений истинно (оператор OR)

Приведем несколько примеров использования выражений.

Фрагмент скрипта, используемый при регистрации нового пользователя. Скрипт проверяет наличие в домашнем каталоге инициализационного скрипта .profile и в случае его отсутствия копирует шаблон:

if [ ! -f $НОМЕ/.profile ]

then

 echo «файла .profile не существует – скопируем шаблон»

 cp /usr/lib/mkuser/sh/profile $НОМЕ/.profile

fi

Фрагмент скрипта, проверяющего наличие новой почты в почтовом ящике пользователя

if [ -s $MAIL ]

then

 echo «Пришла почта»

fi

Фрагмент скрипта инициализации системы – запуска "суперсервера" Internet inetd(1M). Если исполняемый файл /etc/inetd существует, он запускается на выполнение.

if [ -х /etc/inetd ]

then

 /etc/inetd

 echo «запущен сервер inetd»

fi

Фрагмент скрипта, анализирующий ввод пользователя, сохраненный в переменной ANSW. Если пользователь ввел 'N' или 'n', скрипт завершает свою работу.

if [ «$ANSW» = "N" -о «$ANSW» = "n" ]

then

 exit

fi

Циклы

Язык программирования Bourne shell имеет несколько операторов цикла. Приведем их синтаксис:

1) while условие

do

 command1

 command2

 ...

done

2) until условие

do

 command1

 command2

 ...

done

3) for var in список

do

 command1

 command2

 ...

done

С помощью оператора while команды command1, command2 и т.д. будут выполняться, пока условие не станет ложным. Как и в случае с оператором if, условие генерируется кодом возврата команды, например, test.

В случае оператора until команды command1, command2 и т.д. будут выполняться, пока условие не станет истинным.

Оператор for обеспечивает выполнение цикла столько раз, сколько слов в списке. При этом переменная var последовательно принимает значения, равные словам из списка. Список может формироваться различными способами, например как вывод некоторой команды (`имя_команды_формирующей_список`) или с помощью шаблонов shell.

В другой форме for, когда список отсутствует, переменная var принимает значения позиционных параметров, переданных скрипту.

Чтобы наглядно представить себе приведенные операторы, обратимся к конкретным примерам.

Например, скрипт монтирования всех файловых систем /etc/mounall для системы Solaris 2.5 включает в себя их проверку, исходя из данных, указанных в файле /etc/vfsck. При этом используется оператор while.

#

cat /etc/vfsck |

while read special fsckdev mountp fstype fsckpass automnt mntopts

# Построчно считывает записи файла vfsck и присваивает переменным spe-

# cial, fsckdev и т.д. значения соответствующих конфигурационных полей.

do

 case $special in

 '# ' * | '' ) # Игнорируем комментарии

  continue ;;

 '-') # Игнорируем строки, не требующие действия

  continue ;;

 esac

 # Последовательно проверяем файловые системы с помощью утилиты

 # /usv/sbin/fsck

 /usr/sbin/fsck -m -F $fstype $fsckdev >/dev/null 2>&1

 ...

done

Скрипт очистки давно не используемых файлов во временных каталогах (обычно он запускается при загрузке системы) использует оператор for.

for dir in /tmp /usr/tmp /home/tmp

do

 find $dir ! -type d -atime +7 -exec rm {} ;

done

При этом удаляются все файлы в указанных каталогах (/tmp, /usr/tmp и /home/tmp), последний доступ к которым осуществлялся более недели назад.

Селекторы

Оператор case предоставляет удобную форму селектора:

case слово in

шаблон1)

 command

 ...

 ;;

шаблон2)

 command

 ...

 ;;

*)

 command

 ...

 ;;

esac

Значение слово сравнивается с шаблонами, начиная с первого. Если совпадение найдено, то выполняются команды соответствующего раздела, который заканчивается двумя символами ';'. Шаблоны допускают наличие масок, которые были рассмотрены нами в разделе «Подстановки, выполняемые командным интерпретатором». Раздел с шаблоном '*' аналогичен разделу default в синтаксисе селектора switch языка С: если совпадения с другими шаблонами не произошло, то будут выполняться команды раздела '*)'. В качестве примера использования селектора приведем скрипт запуска и останова системы печати в SCO UNIX.

state=$1

set `who -r`

case $state in

'start')

 if [ $9 = "2" -o $9 = "3" ]

 then

  exit

 fi

 [ -f /usr/lib/lpshed ] && /usr/lib/lpshed

 ;;

'stop')

 [ -f /usr/lib/lpshut ] && /usr/lib/lpshut

 ;;

*)

 echo «usage $0 start|stop»

 ;;

esac

В случае, когда скрипт вызван с параметром start, будет произведен запуск системы печати. Если параметр скрипта – stop, то система печати будет остановлена. Запуск скрипта с любым другим параметром приведет к выводу сообщения об ошибке.

Ввод

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

Команда read считывает строку из стандартного потока ввода и последовательно присваивает переменным, переданным в качестве параметров, значения слов строки. Если число слов в строке превышает число переменных, то в последней переменной будут сохранены все оставшиеся слова. Продемонстрируем это на простом примере:

Текст скрипта test5.sh:

#!/bin/sh

echo "input: "

while read var1 var2 var3

do

 echo var1=$var1

 echo var2=$var2

 echo var3=$var3

 echo "input: "

done

Запуск скрипта

$ test5.sh

input: пример работы команды read

var1=пример

var2=работы

var3=команды read

input: еще пример

var1=еще

var2=пример

var3=

input: ^D

$

В приведенном примере read в цикле считывает пользовательский ввод. Цикл завершается, когда достигнут конец файла (что эквивалентно пользовательскому вводу <Ctrl>+<D>), поскольку при этом read возвращает неудачу (код возврата равен 1) и while завершает работу. В первом цикле число введенных слов превышает количество переменных, поэтому значение переменной var3 состоит из двух слов. Во втором цикле значение var3 пусто.

Система управления заданиями

Командный интерпретатор может поддерживать управление заданиями. Для Bourne shell (/bin/sh), который мы рассматриваем, систему управления заданиями включает парный ему интерпретатор /bin/jsh. В остальном этот интерпретатор имеет те же возможности.

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


Выполняется в текущем режимеЗадание может считывать данные и выводить данные на терминал пользователя
Выполняется в фоновом режимеЗаданию запрещен ввод с терминала. Возможность вывода на терминал определяется дополнительными установками
ПриостановленоЗадание не выполняется

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

%jobid

где jobid может принимать следующие значения:


% или +Текущее задание – самое последнее запущенное или вновь запущенное задание
- Предыдущее задание (по отношению к текущему)
?строка Задание, для которого строка присутствует в командной строке запуска
n Задание с номером n
pref Задание, на которое можно уникально указать префиксом pref, например, команда ls(1), запущенная в фоновом режиме, адресуется заданием %ls

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


bg [%jobid] Продолжает выполнение остановленного задания в фоновом режиме. Без параметра относится к текущему заданию.
fg [%jobid] Продолжает выполнение остановленного задания в текущем режиме. Если задание jobid выполнялось в фоновом режиме, команда перемещает его в текущий режим.
jobs [-p | -l] [%jobid ... ] Выводит информацию об остановленных и фоновых заданиях с указанными номерами. Если последний аргумент опущен, выводится информация обо всех остановленных и фоновых заданиях. Приведенные ниже опции изменяют формат вывода: -l Вывести идентификатор группы процессов и рабочий каталог.  Вывести только идентификатор группы процессов.
kill [-signo] %jobid Обеспечивает те же возможности, что и команда kill(1), но по отношению к заданиям.
stop %jobid Останавливает выполнения фонового задания.
wait %jobid Ожидает завершения выполнения задания jobid и возвращает его код возврата.

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

$ inf.j &

[1] 9112

$ comm1 &

[2] 9113

$ jobs

[1] – Running inf.j

[2] + Running comm1

$ stop %1

$ jobs

[1] – Stopped (signal) inf.j

[2] + Running comm1

$ stop %%

$ jobs -1

[1] – 9112 Stopped (signal) inf.j (wd: /home/andy/SH//JOB)

[2] + 9113 Stopped (signal) comm1 (wd: /home/andy/SH/JOB)

$ bg %1

[1] inf.j &

$ jobs

[1] + Running inf.j

[2] – Stopped (signal) comm1

$ kill %1 %2

$ jobs

[1] + Done(208) inf.j

[2] – Done (208) comm1

$

Основные утилиты UNIX

В предыдущих разделах мы использовали некоторые утилиты UNIX. Ниже приводятся краткие характеристики утилит, выпавших из поля нашего зрения. Более подробно с различными утилитами можно познакомиться в электронном справочнике man(1).

Утилиты для работы с файлами

Поле [opt] содержит конкретные опции каждой утилиты.


cd [dir] Изменяет текущий каталог. При задании без параметра – производит переход в домашний каталог пользователя.
cmp [opt] file1 file2 Утилита cmp(1) сравнивает два файла, указанных в качестве аргументов. Если файлы одинаковы, никакого сообщения не выводится. В противном случае выводятся данные о первом несоответствии между этими файлами (в данном примере первое различие найдено в 13-м символе 4-й строки): cat file1 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ cat file2 1 2 3 4 5 6 diff1 7 8 9 10 11 12 13 14 15 diff2 $ cmp file1 file2 file1 file2 differ: char 13, line 4
diff [opt] file1 file2 Утилита diff(1) также сравнивает два файла и выводит список изменений, которые необходимо внести в содержимое этих файлов для того, чтобы преобразовать первый файл во второй. По существу, вывод утилиты diff(1) представляет собой команды редактора ed(1), необходимые для преобразования file1 в file2: $ diff file1 file2 3a4 > diff1 5c6, 7 < 11 12 13 14 15 ... > 11 12 13 14 15 diff2
cp [opt] file1 file2 cp [opt] file1 ... dir Утилита cp(1) служит для копирования файлов. При этом создается не жесткая связь, а новый файл: cp file1 file2ls -li file1 file2 261425 -rw-r–r– 1 andy user 49 Dec 24 12:58 file1 261427 -rw-r–r– 1 andy user 49 Dec 24 13:13 file2
mv [opt] file1 file2 mv [opt] file1 ... dir Утилита mv(1) изменяет имя файла. Если последний параметр является каталогом, то число аргументов утилит cp(1) или mv(1) может превышать 2. В этом случае будет производиться копирование или перемещение указанных файлов в каталог.
rm [opt] file1... rmdir dir1... Утилиты удаления файлов и каталогов. При этом удаляются только записи имен файлов в соответствующих каталогах, фактическое содержимое файла (метаданные и дисковые данные) будет удалено, если число жестких связей для файла станет равным 0.
ls [opt] [file1 file2 ...] Без параметров утилита ls(1) выводит имена файлов текущего каталога. В качестве параметров можно задать имена каталогов, содержимое которых необходимо вывести, или имена файлов, информацию о которых нужно получить. Опции утилиты позволяют получить список различной информативности и формата.
ln [opt] source target Утилита ln(1) создает жесткую связь имени source с файлом, адресуемым именем target. При использовании опции -s будет создана символическая связь.
mkdir [-m mode] [-p] dir1... Создать каталог.
pwd Вывести имя текущего каталога.
fgrep [opt] <подстрока> file1... Утилиты поиска фрагментов текста в файлах. Могут использоваться в качестве фильтров в программных каналах. Для поиска подстроки в файлах можно использовать самую простую из утилит fgrep(1) (fast grep). Если подстрока поиска содержит пробелы или знаки табуляции, ее необходимо заключить в кавычки. Если подстрока уже содержит кавычки, их надо экранировать, поместив символ '' непосредственно перед кавычками: fgrep «рассмотрим в разделе »Создание процесса"" chap* Если вы хотите сделать поиск нечувствительным к заглавным/строчным символам, используйте ключ . Для поиска строк, не содержащих указанную подстроку, используется ключ -v.
grep [opt] <рег_выражение> file1... egrep [opt]  <рег_выражение> file1... Утилиты grep(1) и egrep(1) позволяют производить более сложный поиск, например, когда вы не уверены в написании искомого слова, или хотите найти слова, расположенные в определенных местах файла. В этом случае в качестве подстроки поиска указывается регулярное выражение (рег_выражение). Например, чтобы произвести поиск слова «центр» в американском (center) и британском (centre) написании, можно задать следующую команду: grep «cent[er]» file или grep «cent[er][er]» file [er] является регулярным выражением, соответствующим либо символу 'е', либо 'r'. Регулярное выражение должно быть заключено в кавычки для предотвращения интерпретации специальных символов командным интерпретатором shell.
cat [opt] file Утилиты просмотра содержимого файла. Команда cat file выводит содержимое файла file на экран терминала. Если у вас есть подозрение, что файл не текстовый, т.е. содержит «непечатные» символы, лучше запустить cat(1) с ключом -v. В этом случае вывод таких символов (которые, кстати, могут нарушить настройки вашего терминала) будет подавлен.
more [opt] file pg [opt] file Если размер файла велик и его содержимое не помещается в терминальном окне, удобнее будет воспользоваться утилитами pg(1) и more(1), позволяющими выводить файл порциями.
head [-n] file tail [opt] file Посмотреть только начало (первые n строк) или конец (последние n строк) файла можно с помощью утилит head(1) и tail(1), соответственно.
sort Для сортировки строк файла используется утилита sort(1). Например, для сортировки текста в алфавитном порядке необходимо ввести следующую команду: sort -d file >sorted file Вы можете указать номер слова строки, по которому необходимо произвести сортировку (точнее, номер поля записи; по умолчанию записью является строка, а поля разделены пробелами). Например, для сортировки строк файла file Андрей Май Борис Январь Владимир Март по месяцам, можно использовать команду sort -M +1 file в результате получим: Борис Январь Владимир Март Андрей Май Опция -M определяет сортировку по месяцам (не по алфавиту), опция +1 указывает, что сортировку необходимо проводить по второму полю каждой строки.
cut Позволяет отфильтровать указанные поля строк файла. Разделитель полей указывается опцией -d<sep>. Например, чтобы получить реальные имена пользователей системы (пятое поле файла паролей), можно использовать следующую команду: $ cat /etc/passwd | cut -f5 -d: ... WWW Administrator Yuri Korenev Serge Smirnoff W3 group Konstantin Fedorov Andrei Robachevsky Sergey Petrov
wc file Позволяет вывести число строк, слов и символов текста файла.
find dir [opt] Выполняет поиск файла в файловой системе UNIX, начиная с каталога dir. Например, для вывода полного имени исполняемого файла командного интерпретатора Bourne shell введите команду: find / -name sh -print 2>/dev/null /usr/bin/sh /usr/xpg4/bin/sh /sbin/sh С помощью опции -name указывается имя искомого файла, а с помощью опции -print – действие (вывести полное имя). С помощью find(1) можно производить поиск файлов по другим критериям, например, размеру, последнему времени модификации и т.д. Например, чтобы найти файлы с именем core (образ процесса, создаваемый при неудачном его завершении и используемый в целях отладки), последнее обращение к которым было, скажем, более месяца назад (скорее всего такие файлы не нужны пользователям и только «засоряют» файловую систему), можно задать команду: find / -name core -atime +30 -print /u/local/lib/zircon/lib/core /u/local/etc/httpd/data/zzmaps/core /home/amd/WORK/novosti/core /home/amd/WORK/access/core /home/guests/snell/core Если вы сторонник жесткого администрирования, то можно применить следующую команду: find / -name core -atime +30 -exec rm {} ; которая автоматически удалит все найденные файлы.
chown user file ... Изменяет владельца-пользователя указанных файлов.
chgrp group file ... Изменяет владельца-группу указанных файлов.
chmod mode file ... Изменяет права доступа и дополнительные атрибуты файлов.
file file1 ... Сканирует начало файла и пытается определить его тип. Если это текстовый файл (ASCII), file(1) пытается определить его синтаксис (текст, программа на С и т.д.). Если это бинарный файл, то классификация ведется по так называемому magic number, определения которого находятся в файле /etc/magic. file * nlc-2.2d.tar: tar archive report.doc: ascii text work: directory runme.c: с program text runme: ELF 32-bit MSB executable figure.gif: data

    Ваша оценка произведения:

Популярные книги за неделю