Строки. Строковые константы.

Содержание

1. Задание

2. Теоретическая часть

3. Краткое описание программы

4. Код программы

5. Скриншоты

6. Выводы


Задание

Написать программу, которая обеспечивает работу команды вида:
[имя_команды] [-a |-b] [-o исходный файл],
где-a и-b несовместимые флаги.
• если задан флаг-a - команда отображает имя домашнего директории, в котором пользователь оказывается после входа в систему;
• если задан флаг-b - имя терминала.
Полученная информация также записывается в файл, имя которого задается как аргумент опции-o.


 

Теоретическая часть

Открытие и создание файла.

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

#include <fcntl.h.>

#include<sys/types.h

#include<sys/stat.h>

int open(char *filename, int flags, [mode_t mode]);

char *filename – символьная строка , содержащая имя открываемого файла (путь к нему).

int flags – указывает способ открытия файла.

[mode_t mode] – необязательный параметр режима доступа для создаваемого файла. Обычно для задания прав доступа к файлу используют восьмеричную систему представления прав доступа. Но в POSIX также есть следующий набор констант, которые можно объединять в аргументе mode_t mode, применяя к ним операцию бинарного или:

S_IRWXU – у владельца есть разрешение на чтение, запись и выполнение;

S_IRUSR – у владельца есть разрешение на чтение;

S_IWUSR – у владельца есть разрешение на запись;

S_IXUSR – у владельца есть разрешение на выполнение;

S_IRWXG – у группы есть разрешение на чтение, запись и выполнение;

и тому подобные.

Эти константы режима файла объявлены в заголовочном файле sys/stat.h. Поэтому для их использования необходимо его подключить.

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

Флаги открытия файла представляют собой набор символических констант, которые могут соединяться операцией побитового или (||). Чтобы использовать эти флаги и системный вызов open(), в программу необходимо включить заголовочный файл fcntl.h.

Основные флаги для системного вызова open(), объявленные в заголовочном файле fcntl.h:

O_RDONLY – открыть файл только для чтения;

O_WRONLY – открыть файл только для записи;

O_RDWR – открыть файл для чтения и записи;

O_CREAT – создать файл, если он не существует;

O_TRUNC – очистить файл, если он существует. То есть файл усекается до нулевой длины;

O_APPEND – дописать в существующий файл. Перед каждой записью указатель позиции в файле устанавливается на конец файла, тем самым позволяя дописать в файл. Все данные будут записаны в конец файла, даже если текущее смещение было восстановлено с помощью lseek();

O_EXCL – не открывать файл, если он существует (используется с флагом O_CREATE)

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

Следует отметить, что некоторые флаги не могут объединяться между собой. Здесь нужно рассуждать логически. Нельзя, например, соединить флаги O_RDONLY и O_WRONLY, поскольку они противоречат друг другу.

Возвращаемое open() значение является либо новым дескриптором файла, либо -1, означающим ошибку, в этом случае для переменной errno будет установлено подходящее значение ошибки.

Окружение.

Окружение (environment) – это набор специфичных для конкретного пользователя пар переменная=значение. Эти пары называют переменными окружения. Чтобы посмотреть что собой представляет окружение в действительности, можно использовать следующую команду:

$ env

На экран будет выведено окружение программы env. В современных Linux-дистрибутивах окружение представлено десятками переменных, но широко используются только некоторые из них:

USER – имя пользователя;

HOME – домашний каталог;

PATH – список каталогов, в которых осуществляется поиск исполняемых файлов программ;

SHELL – используемая командная оболочка;

PWD – текущий каталог.

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

$ echo $VARIABLE

Здесь VARIABLE – это имя переменной. Вообще говоря, оболочка вместо записи $VARIABLE всегда подставляет значение соответствующей переменной окружения.

Программа может читать окружения двумя способами:

1. Посредством внешней переменной environ, представляющей собой массив строк ПЕРЕМЕННАЯ=ЗНАЧЕНИЕ. Эта переменная объявлена в заголовочном файле unistd.h.

2. При помощи функции getenv(), которая возвращает значение указанной переменной окружения. Эта функция объявлена в заголовочном файле stdlib.h

Фнукция getenv() используется для получения значения конкретной переменной.

Синтакис:

#include <stdlib.h>

char *getenv(const char *name);

Ее аргументом является имя переменной name окружения, которую нужно искать, такое как «HOME» или «PATH». Если такая переменная существует, getenv() возвращает указатель на строковое значение. Если нет, возвращается NULL.

Внешняя переменная environ обычно служит для просмотра набора переменных окружения предоставляет доступ таким же способом, как argv предоставляет доступ к аргументам командной строки. Необходимо самим объявлять переменную. Хотя она и стандартизирована POSIX, environ намеренно не объявлена ни в одном стандартном заголовочном файле. Объявление

extern char **environ;

Как и в argv, завершающим элементом environ является NULL. Однако, здесь нет переменной «числа строк окружения», которая соответствовала бы argc.

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

Можно получить доступ к окружению, объявив третий параметр main():

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

Затем можно использовать envp также, как environ.

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

int setenv(const char *name, const char *value, int overwrite); // установить переменную окружения

int putenv(char *string); // установить переменную окружения, использует строку.

void unsetenv(const char *name); // удалить переменную окружения.

int clearenv(void); // очистить все окружение.

Разбор командной строки.

Для разбора опций и их аргументов средствами языка С служит функция getopt() и ассоциированные с ней внешние переменные. Функция getopt() читает командную строку и возвращает имя очередной опции из цепочки опций, указанных в строке optstring.

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

#include<unistd.h>

int getopt(int argc, char *const argv[], const char *optstring);

extern char *optarg;

extern int optind, opterr, optopt;

- аргументы argc (количество аргументов командной строки) и argv (массив указателей на эти аргументы) задают командную строку в том виде, в котором она передается функции main();

- optstring является цепочкой имен опций;

- переменная optind (c начальным значением 1) служит индексом в массиве argv[].

Получая на вход число параметров команды (argc), массив указателей на эти параметры (argv) и строку с опциями (optstring), getopt() возвращает первый параметр и задает некоторые внешние переменные. При повторном вызове с теми же аргументами функция вернет следующий параметр и задаст внешнюю переменную. Если больше не будет найдено опций, которые надо распознать, то функция вернет -1, что означает завершение обработки команды.

Внешние переменные, задаваемые функцией getopt(), включают в себя:

- optarg – указатель на текущий аргумент, если таковой имеется (строковое значение аргумента).

- optind – индекс на следующий указатель argv, который будет обработан при следующем вызове getopt().

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

Строка с опциями optstring состоит из одного символа для каждого аргумента командной строки.

Можно вызывать getopt() до тех пор, пока она не вернет -1; любые остальные аргументы команды обычно являются именами файлов или чем-либо другим, необходимым для программы.

Для использования getopt() ее вызывают повторно в цикле, до тех пор, пока она не вернет -1. Каждый раз обнаружив действительный символ опции, функция возвращает этот символ. В результате функция getopt() возвращает имя очередной опции с числа перечисленных в цепочке optstring (если такое удалось выделить). Если за какой либо буквой в строке optstring следует двоеточие, значит эта опция принимает аргумент. По наличию у опции аргумента показатель на него помещается в переменную optarg с соответствующим увеличением значения optind. Если у опции аргумент отсутствует, а на первом месте в optstring задано двоеточие, оно и служит результатом. Если встретилось имя опции, не перечисленное opstring, или у опции нет аргумента, а на первом месте в opstring задано не “:”, то результатом станет “?”.

В любой из перечисленных выше ошибочных ситуациях в переменную optopt помещается имя «проблемной» опции. Кроме того, в стандартный протокол выдается диагностической сообщение (обычно getopt печатает сообщение об ошибке, когда находит неверный параметр. Присваивание нуля переменной opterr выключает это свойство). Для подавления выдачи следует присвоить переменной opterr нулевое значение. То есть если getopt()не распознала символ опции, то она выводит на стандартный поток ошибок соответствующее сообщение, заносит символ в optopt и возвращает "?". Вызывающая программа может предотвратить вывод сообщения об ошибке, установив нулевое значение opterr.

Когда переменная opterrне равна нулю (по умолчанию так и есть), getopt() сама выводит сообщения в случае недействительной опции или отсутствия аргумента. Если же opterrравен нулю, то в случае возникновения ошибки getopt() возвращает "?" или ":" в зависимости от того найдена

 

недействительная опция или пропущен обязательный аргумент опции, в переменной optoptбудет находится обнаруженный недействительный символ.

Когда все аргументы с опциями обработаны, переменная optind указывает, где в конца массива argv можно найти оставшиеся аргументы командной строки. И программа может вывести все оставшиеся аргументы, начиная с номера, хранящегося в переменной optind.

Наконец, если при вызове getopt() показатель argv[optind] не определяет начало опции (например, он пуст или первый символ указанной цепочки отличен от знака минус), результат равный -1 как признак того, что разбор опций закончен.

Закрытие файла.

Системный вызов close() закрывает файл: его элемент в системной таблице дескрипторов файлов помечается как неиспользуемый, и с этим дескриптором нельзя производить никаких дальнейших действий. То есть этот вызов освобождает файловый дескриптор. Системный вызов close() объявлен в файле unistd.h следующим образом:

#include <unistd.h>

int close (int fd);

В случае успеха возвращается 0, при ошибке возвращается -1. При возникновении ошибки нельзя ничего сделать, кроме сообщения о ней. Код ошибки присваивается переменной errno.

Запись в файл.

Вызов write() представляет собой эквивалент read() и также определен в POSIX:

#include<unistd.h>

#include<sys/types.h>

ssize_t write(int fd, const void *buf, size_t size);

При вызове write(), начиная с текущей позиции в файле, указанном при помощи файлового дескриптора fd, в него записывается до size байтов из буфера buf. Файлы, представляющие объекты, которые не поддерживают поиск, всегда записываются начиная с «головы».

В случае успеха возвращается количество записанных байтов, а позиция в файле соответствующим образом обновляется. В случае ошибки возвращается значение -1 и соответствующим образом устанавливается переменная errno. Вызов write() может вернуть значение 0, но оно всего лишь указывает, что было записано ноль байт.

Строки. Строковые константы.

Строкова константа, строковый литерал, или просто литерал – это последовательность из нескольких (в частном случае ни одного) символов, заключенных в двойные кавычки. Кавычки не являются частью строки, а только ограничивают ее. Формально строковая константа как и строка является массивом символов. Во внутреннем представлении строки в конце присутствует нулевой символ ‘\0’, так что физический объем памяти для хранения строки превышает количество символов, записанных между кавычками, на единицу. Это означает, что на длину строки не накладываются никакие ограничения, но

 

 

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

Функция sprintf().

Функция sprintf() выполняет те же преобразования, что и printf, но помещает результат вывода в символьную строку.

Синтаксис:

#include <stdio.h>

int sprintf(char *string, const char *format, apr1, apr2, … )

Функция sprintf() форматирует аргументы apr1, apr2 и т.д., согласно строке формата format, но результат помещается не в поток вывода (stdout), а в строку string (массив выводимых данных). Предполагается, что string имеет достаточную для этого длину.

Функция strlen().

Строка рассматривается как массив символов типа char, заканчивающийся нулевым байтом. Нулевой байт – это байт, каждый бит которого равен нулю. Для него введен специальный символ ‘\0’. Это следует учитывать при описании соответствующего массива символов. То есть если массив хранит 10 символов, то нужно предусмотреть 11 символ – нулевой байт.

Синтаксис:

#include <string.h>

int strlen(char *string);

Для определения длины строки в Си существует функция strlen(). Стандартная библиотечная функция strlen() возвращает количество символов в строке, переданной ей в качестве аргумента, не считая завершающего ‘\0’. Функция strlen() и другие функции для работы со строками объявлены в стандартном заголовочном файле <string.h>.

Функция strlen( ) позволяет определять точную длину строки числом символов. Результат операции sizeof оказывается на единицу большим, поскольку при этом учитывается и "невидимый" нуль-символ, помещенный в конец строки. Мы не указываем компилятору, какой объем памяти он должен отвести для размещения всей фразы, он сам подсчитывает число символов между кавычками.

Функция getenv() ищет в списке окружения строку, совпадающую со строкой, указанной вname. Строки имеют вид имя = значение.

Функция getenv() возвращает указатель на значение в окружении или NULL, если ничего не найдено. Sysinfo - возвращает общесистемную статистику.

Uname - сообщает информацию о данном компьютере и операционной системе.

Системные вызовы нужны для работы с системой, они работают напрямую с ядром.

Краткое содержание программы

 

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

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

 


 

Код программы

#include<unistd.h>

#include<stdio.h>

#include<stdlib.h>

#include<fcntl.h>

#include<sys/types.h>

#include<sys/stat.h>

 

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

{

int t;

int c;

int aflag=0;

int bflag=0;

int errflag=0;

int flag='?';

char*ofile=NULL;

opterr=0;

if (argc!=4)

{ printf("Использование:%s:[-a|-b][-o файл вывода]\n",argv[0]);

return 1;

}

 

while((c=getopt(argc,argv,":abo:"))!=-1)

{

switch(c)

{

case 'a':aflag++;

 

flag=c;

if(bflag)

{

printf("Опции a и b несовместимы\n");

errflag++;

}

break;

case 'b':bflag++;

 

flag=c;

if(aflag)

{

printf("Опции a и b несовместимы\n");

errflag++;

}

break;

 

case 'o':ofile=optarg;

break;

 

case':':printf("Отсутствует аргумент опции -%c\n",optopt);

errflag++;

break;

}

 

}

if(errflag)

{(void)printf("Использование:%s:[-a|-b][-o файл выхода]\n",argv[0]);

return(1);

}

 

printf("Заданный флаг:%c\n",flag);

printf("Аргумент опции o: %s\n",ofile);

printf("Остаток командной строки: ");

 

char pr;

printf("Каким способом Вы хотите выполнить лабу?");

printf("Вы хотите воспользоваться функцией библиотеки [m] /Вы хотите воспользоваться системными вызовами [g] \n");

scanf("%c", &pr);

if (pr=='m')

{

 

for (;optind<argc;optind++)

{

printf("%s",argv[optind]);

}

printf("\n");

 

{

int outfile;

char buf[100];

 

switch(flag)

{

case 'a':

printf("\n");

 

system("echo 'Imya directorii v kotoroy okazivaetsya koristuva4 posle zapuska v sistemu: ';cd; pwd");

sprintf (buf,"pwd > %s",argv[optind-1]);

system(buf);

 

break;

case 'b':

system("echo 'Imya terminala: '; tty");

 

sprintf(buf,"tty > %s",argv[optind-1]);

system(buf);

break;

}

}

}

else

 

{

 

for (;optind<argc;optind++)

{

printf("%s",argv[optind]);

}

printf("\n");

 

char * var;

char buf[100];

 

switch(flag)

{

case 'a':

var=getenv ("HOME");

printf("Адресс домашней директории:%s\n",var);

sprintf(buf,"Адресс домашней директории:%s\n",var);

 

break;

case 'b':

var=getenv ("TERM");

printf("Имя терминала:%s\n",var);

sprintf(buf,"Имя терминала:%s\n",var);

break;

}

 

char t;

 

int outfile;

 

outfile=open(argv[optind-1],O_RDWR,0644);

if (outfile!=-1)

{

printf("\nФайл уже существует! \n Добавить текущую информацию в конец файла(если да нажмите [c]? \n Перезаписать? [r]");

scanf("%c",&t);

scanf("%c",&t);

if (t=='y')

lseek(outfile,0,SEEK_END);

}

else if (outfile==-1)

outfile=open(argv[optind-1],O_RDWR|O_CREAT,0644);

write(outfile,buf,100);

 

{

 

printf("\n");

close(outfile);

}

}

return (0);

}

 


 

Скриншоты

 

 

 


 

Выводы

Приобрел навыки работы с командной строкой, с файлами и с переменными среды окружения Linux.

В частности научился использовать uname, sysinfo и функции getenv и system.

Функция system передает заданную строку string к интерпретатору команд и обрабатывает эту строку как команду Linux.