- Давайте напишем командную оболочку Linux
- Приветствие
- Что должна уметь наша командная оболочка
- Как устроена работа командной оболочки
- Немного про системный вызов fork()
- Подробнее про exec()
- Перейдем к полноценной реализации
- Часть 1. Чтение строки с консоли
- Часть 2. Разбиение строки на токены
- Часть 3. Выполнение процессов
- Структура хранения списка запущенных процессов
- Вспомогательные функции добавления процессов
- Вспомогательные функции для завершения процессов
- Непосредственно запуск процессов
- Вспомогательные функции для командной оболочки.
- Реализация вспомогательных функций
- Часть 4. Главный цикл командной оболочки
- Часть 5. Итоговый результат
- ИТ База знаний
- Полезно
- Навигация
- Серверные решения
- Телефония
- Корпоративные сети
- Учимся писать базовые скрипты в Unix и Linux
- Идентификация оболочки.
- Выбор оболочки
- Выполнение команд
- Добавление комментариев
- Делаем файл исполняемым
- Использование команды if
- Понятие переменных
- Запрос пользователя на ввод данных
- Использование аргументов командной строки
- Различные способы создания циклов
- Использование оператора case
- Реакция на ошибки
Давайте напишем командную оболочку Linux
Приветствие
Всем привет! Хочу поделиться своим опытом написания собственной командной оболочки Linux используя Posix API, усаживайтесь поудобнее.
Итоговый результат
Что должна уметь наша командная оболочка
Запуск процессов в foreground и background режиме
Завершение background процессов
Поддержка перемещения по директориям
Как устроена работа командной оболочки
Считывание строки из стандартного потока ввода
Разбиение строки на токены
Создание дочернего процесса с помощью системного вызова fork
Замена дочернего процесса на необходимый с помощью системного вызова exec
Ожидание завершения дочернего процесса (в случае foreground процесса)
Немного про системный вызов fork()
Простыми словами системный вызов fork создает полный клон текущего процесса, отличаются они лишь своим идентификатором, т. е. pid .
Что выведет данная программа:
I’m parent process!
I’m child process!
Что же произошло? Системный вызов fork создал клон процесса, т. е. теперь мы имеем родительский и дочерний процесс.
Чтобы отличить дочерний процесс от родительского в коде достаточно сделать проверку. Если результат функции fork равен 0 — мы имеем дело с дочерним процессом, если нет — с родительским. Это не означает, что в операционной системе id дочернего процесса равен 0 .
Причем, порядок выполнения дочернего и родительского процесса ничем не задекларирован. Все будет зависеть от планировщика операционной системы. Поэтому в конце блока родительского процесса добавлена строчка wait(NULL) , которая дожидается окончания дочернего процесса.
Подробнее про exec()
В документации есть различные вариации системного вызова exec , но они отличаются только способом передачи токенов в параметры функции, смысл от этого не изменяется.
Системный вызов exec заменяет текущий процесс сторонним. Естественно, сторонний процесс задается через параметры функции.
Что выведет данная программа
total 16
-rwxr-xr-x 1 runner runner 8456 Jan 13 07:33 main
-rw-r—r— 1 runner runner 267 Jan 13 07:33 main.c
Родительский процесс как обычно ожидает завершения дочернего процесса. В это время после системного вызова exec происходит замена дочернего процесса на консольную утилиту ls , она была взята для примера.
Можно сказать мы реализовали простую командную оболочку, вся логика заключается именно в этом.
Перейдем к полноценной реализации
Часть 1. Чтение строки с консоли
Изначально нам надо уметь считывать строку из командной строки. Думаю, с этим не возникнет сложностей.
В данной функции происходит чтение строки с применением функции getline . После чего, если в конце строки имеется символ переноса строки, удаляем его.
Обратите внимание, что при чтении могут возникнуть ошибки, одна из них — нажатие сочетания клавиш ctrl-D . Однако это штатный случай завершения работы командной оболочки, следовательно в данном случае не должна выводиться ошибка.
Часть 2. Разбиение строки на токены
В данной части будет представлена реализация разбиения строки на массив токенов.
Определения начальной длины массива и разделителей строки.
Код выглядит довольно громоздким, однако в нем нет ничего сложного.
Очередной токен получается с использованием функции strtok . После чего данный токен копируется в массив токенов. Если в массиве токенов не достаточно места, массив увеличивается в 2 раза.
Завершается всё добавлением завершающего токена равного NULL , т. к. функция exec() ожидает наличие данного завершающего токена.
Часть 3. Выполнение процессов
Структура хранения списка запущенных процессов
Напишем определения структур для foreground и background процессов, fg_task и bg_task . А также определение структуры для хранения всех процессов tasks .
Создадим в коде глобальную переменную типа tasks , которая и будет хранить все наши запущенные процессы.
Вспомогательные функции добавления процессов
Установка foreground процесса выглядит банально и не нуждается в комментировании.
Добавление background процесса выглядит посложнее.
На деле же все проще. Смотрим есть ли место в массиве для хранения информации о процессе. Если его недостаточно — увеличиваем длину массива в 2 раза. После чего происходит добавление структуры bg_task в массив и последующее заполнение полей структуры информацией о процессе.
Данная функция возвращает -1 в случае неудачи.
Вспомогательные функции для завершения процессов
Добавим функцию экстренного завершения foreground процесса. Данная функция с помощью системного вызова kill с параметром SIGTERM завершает процесс по id процесса.
Также добавим функцию для завершения background процесса.
Данная функция принимает в себя массив токенов вида <"term", " ", NULL>. После чего преобразует токен индекса background задачи в число. Убивает background задачу посредством системного вызова kill .
Непосредственно запуск процессов
Для удобства введем функцию is_background , определяющую является ли задача фоновым процессом. Данная функция просто проверяет наличие & в конце.
Введем функцию launch которая будет запускать background процесс если в конце присутствует токен & , иначе будет запускаться foreground процесс.
То, что происходит в этой функции уже должно быть все понятно.
Создается дубликат процесса с помощью системного вызова fork
Заменяем дочерний процесс на требуемый с помощью системного вызова exec
Определяем является ли процесс фоновым
Если процесс фоновый — просто добавляем его в список bacground задач
Если процесс не фоновый — дожидаемся окончания выполнения процесса
В функции присутствует неизвестная функция quit . Ее мы разберем в следующем блоке.
Вспомогательные функции для командной оболочки.
Введем функцию execute , которая в зависимости от первого токена выбирает нужное действие.
Данная функция пропускает действие, если первый токен NULL . Смена директории, если первый токен cd . Вывод справки об использовании, если первый токен help . Завершение работы командной оболочки, если первый токен quit . Вывод списка background задач, если первый токен bg . Завершение процесса по индексу, если первый токен term .
Во всех других случаях запускается процесс.
Реализация вспомогательных функций
Значение CONTINUE означает дальнейшее исполнение главного цикла командной оболочки. Значение EXIT прерывает выполнение главного цикла программы.
Функция quit отключает все callback функции по событию SIGCHLD — т. е. функции, выполняющиеся когда дочерний элемент был завершен. После этого завершает все активные процессы.
Основные цвета командной оболочки.
Часть 4. Главный цикл командной оболочки
Здесь и происходит вся магия. Взгляните на следующие строки. С помощью функции signal задаются callback функции на заданные события.
Событие SIGINT — срабатывает при нажатии комбинации ctrl-C , которое в дефолтном поведении завершает работу программы. В нашем же случае мы переназначаем его на завершение foreground процесса.
Событие SIGCHLD — срабатывает при завершении дочернего процесса созданyого с помощью системного вызова fork . В нашем случае мы переопределяем его на пометку фоновой задачи как выполненной с помощью функции mark_ended_task .
Все что описано в главном цикле командной оболочки можно описать словами:
Вывод информации о пользователе и текущей директории с помощью функции display
Чтение строки из стандартного потока ввода
Разбиение строки на токены
Выполнение ранее описанной функции execute , которая в зависимости от массива токенов выполняет нужное нам действие.
Нам осталось реализовать одну оставшуюся функцию display . Которая получает информацию о текущей директории с помощью функции getcwd и имя пользователя с помощью функции getpwuid .
Часть 5. Итоговый результат
Надеюсь, данный материал полезен. Если вам есть чем дополнить данный материал, буду рад услышать ваши мысли в комментариях.
С исходным кодом проекта вы можете ознакомиться по данной ссылке.
Источник
ИТ База знаний
Курс по Asterisk
Полезно
— Узнать IP — адрес компьютера в интернете
— Онлайн генератор устойчивых паролей
— Онлайн калькулятор подсетей
— Калькулятор инсталляции IP — АТС Asterisk
— Руководство администратора FreePBX на русском языке
— Руководство администратора Cisco UCM/CME на русском языке
— Руководство администратора по Linux/Unix
Навигация
Серверные решения
Телефония
FreePBX и Asterisk
Настройка программных телефонов
Корпоративные сети
Протоколы и стандарты
Учимся писать базовые скрипты в Unix и Linux
Если вы еще не умеете писать скрипты в системах Unix и Linux, эта статья познакомит с основами написания скриптов.
Написание скриптов в системе Unix или Linux может быть одновременно и простым и чрезвычайно сложным. Все зависит от того, что вы пытаетесь выполнить, используя скрипт. В этой статье мы рассмотрим основы создания скриптов — то, как начать, если вы никогда раньше не создавали скрипты.
Онлайн курс по Linux
Мы собрали концентрат самых востребованных знаний, которые позволят тебе начать карьеру администратора Linux, расширить текущие знания и сделать уверенный шаг к DevOps
Идентификация оболочки.
Сегодня в системах Unix и Linux есть несколько оболочек, которые вы можете использовать. Каждая оболочка — это интерпретатор команд. Он считывает команды и отправляет их ядру для обработки.
Bash является одной из самых популярных оболочек, но существуют также zsh , csh , tcsh и korn . Есть даже оболочка под названием fish , которая может быть особенно полезна новичкам в Linux благодаря полезным параметрам автозаполнения команд. Чтобы определить, какую оболочку вы используете, используйте эту команду:
Вы также можете определить свою основную оболочку, просмотрев файл /etc/passwd :
Один из способов определить, какие оболочки доступны в системе Linux, — это проверить файл /etc/shells .
На выводе видно, что доступно всего девять оболочек.
Какую оболочку выбрать пользователю во многом зависит от того, что он привык использовать, поскольку большая часть синтаксиса в скриптах не представляет команды, которые вы найдете в /bin , /us /bin или /usr/local/bin . Вместо этого они являются частью самой оболочки и называются «встроенными». Сюда входят команды, используемые для цикла (например, for и while ).
Один из простых вариантов создания скриптов — использовать ту оболочку, которую вы используете в командной строке, поскольку, в конце концов, вам будет более или менее комфортно ее пользоваться.
Выбор оболочки
Чтобы определить, какая из доступных оболочек будет выполнять команды вашего скрипта, в первой строке вашего скрипта пропишите одну из строчек, приведенных ниже:
Когда первая строка вашего скрипта идентифицирует оболочку, которая будет использоваться, эта оболочка будет выполнять команды в скрипте. Если вы не определите оболочку в первой строке в скрипте, то оболочка, которую вы используете при вызове сценария, будет той, которая его запускает.
Выполнение команд
Любую команду, которую вы запускаете в командной строке Linux, можно запустить в скрипте, если он совместим с указанной оболочкой. Используйте свой любимый текстовый редактор и вводите нужные для исполнения команды. Вот очень простой скрипт, который выводит текущую дату в формате день-месяц-год. Скрипт имеет название today .
Чтобы запустить этот скрипт, выполните команду:
Добавление комментариев
Рекомендуется добавлять к скриптам комментарии, объясняющие, для чего предназначен скрипт, особенно если скрипт длинный или имеется сложный синтаксис. Просто начните комментарий со знака # . Комментарии могут быть отдельными строками или добавляться в конец команд в скрипте. Например:
Делаем файл исполняемым
Чтобы сделать скрипт исполняемым, используйте команду chmod и убедитесь, что предполагаемые пользователи могут его запустить. Например:
Если скрипт не сделан как исполняемый, его все равно можно запустить с помощью символа . Это встроенная функция, которая «выполняет» (т. е. читает и запускает) скрипт.
Использование команды if
Команда if позволяет вам проверять условия или переменные. В примере ниже мы проверяем, запускается ли скрипт в пятницу.
Базовый синтаксис команды if — if value == other_value . Знак == выполняет сравнение, и необходимо убедиться, что оболочка видит по одному значению с каждой стороны оператора сравнения. По этой причине часто приходится заключать свои строки в кавычки.
Понятие переменных
Чтобы разобраться в понятии переменной важно понимать, что переменные назначаются одним способом, а на них ссылаются другим. Назначьте переменной только ее имя, но перед именем поставьте знак $ , чтобы ссылаться на нее.
Запрос пользователя на ввод данных
Чтобы пользователь, во время исполнения скрипта, смог ввести некоторую информацию, вам необходимо вывести как подсказку, так и команду, чтобы прочитать, что вводит пользователь. Вы также должны присвоить переменной имя, которое имеет смысловое значение, как в этом примере. Обратите внимание, что использование команды echo -n означает, что пользователь вводит свой ответ в той же строке, что и приглашение.
Человек, запускающий сценарий, увидит приглашение и введет ответ :
Использование аргументов командной строки
Чтобы использовать аргументы, которые пользователь вводит вместе с именем скрипта, необходимо знать, как их идентифицировать. Аргументам скрипта будут присвоены имена $1 , $2 и так далее. Для любого аргумента, который вы собираетесь использовать неоднократно, вы можете рассмотреть возможность присвоения этих значений более значимым именам переменных.
В этом случае мы проверяем, является ли первый предоставленный аргумент числовым, и закрываем скрипт, если это не так. Если ответ — число, то далее назначаем его переменной $loops , чтобы использовать позже в скрипте.
Еще одна полезная вещь, которую нужно сделать в скрипте, — это сначала проверить наличие аргументов. В противном случае синтаксис, подобный показанному выше, не сработает, потому что оболочка увидит выражение if [[! = 3 *]]; , что приведет к синтаксической ошибке.
Чтобы проверить правильность количества предоставленных аргументов, вы можете использовать синтаксис, подобный приведенному ниже, который проверяет, были ли предоставлены по крайней мере два аргумента, и, в противном случае, напоминает пользователю, что требуется как количество строк, так и имя файла:
Различные способы создания циклов
Есть несколько способов сделать цикл внутри скрипта. Используйте for , если вы хотите повторить действие заданное количество раз. Например:
Используйте while , если хотите выполнять какое-то действие, пока условие существует или не существует.
Использование оператора case
Операторы case позволяют вашим скриптам реагировать по-разному в зависимости от того, какие значения проверяются. В приведенном ниже скрипте используются разные команды для извлечения содержимого файла, предоставленного в качестве аргумента, путем определения типа файла.
Обратите внимание, что этот сценарий также запрашивает имя файла, если оно не было предоставлено, а затем проверяет, действительно ли указанный файл существует. Только после этого выполняется извлечение.
Реакция на ошибки
Вы можете обнаруживать ошибки в скриптах и реагировать на них и тем самым избегать других ошибок. Хитрость заключается в том, чтобы проверять выходные коды после запуска команд. Если код выхода имеет значение, отличное от нуля, произошла ошибка. В этом скрипте проверяется, запущен ли Apache, но отправляем результат проверки в /dev/null . Затем проверяем, не равен ли код выхода нулю, поскольку это означает, что команда ps не получила ответа. Если код выхода не равен нулю, сценарий сообщает пользователю, что Apache не запущен.
Онлайн курс по Linux
Мы собрали концентрат самых востребованных знаний, которые позволят тебе начать карьеру администратора Linux, расширить текущие знания и сделать уверенный шаг к DevOps
Источник