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

Командная строка $, которая называется командной строкой, выдается оболочкой. Пока отображается запрос, вы можете ввести команду. Оболочка считывает ваш ввод после того, как вы нажмете Enter. Он определяет команду, которую вы хотите выполнить, глядя на первое слово вашего ввода. Слово - это непрерывный набор символов. Слова разделяются пробелами и табуляциями. Сценарий оболочки - это список команд, которые перечислены в порядке выполнения.

ls - это команда оболочки, которая выводит список файлов и каталогов в каталоге. С параметром -l ls выведет список файлов и каталогов в формате длинного списка.

Итак ... Что ДЕЙСТВИТЕЛЬНО происходит?

Прежде всего, прежде чем вы что-нибудь напечатаете, оболочка выводит вам приглашение, которое обычно заканчивается знаком $. Приглашение или командная строка - это то место, где вы будете вводить свою команду.

1. Отображается приглашение оболочки

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

Во-вторых, после того, как вы наберете команду, оболочка прочитает то, что вы ввели с помощью функции getline.

Функция getline считывает введенную строку как одну строку со стандартного ввода и сохраняет ее в буфере.

Функция getline ()

Последняя и самая модная функция для чтения строки текста - это getline (). Это новая функция библиотеки C, появившаяся примерно в 2010 году.

Возможно, вы не слышали о функции getline (), и некоторые программисты на C избегают ее, потому что она использует - приготовьтесь - указатели! Тем не менее, это хорошая функция линейного ввода, с которой вы должны быть знакомы, даже если не планируете ее использовать.

Вот типичный оператор getline ():

getline(&buffer,&size,stdin);

Прототип функции getline () представлен в файле заголовка stdio.h. Вот три аргумента:

&buffer - это адрес первой позиции символа, в которой будет сохранена входная строка. Это не базовый адрес буфера, а первый символ в буфере. Этот тип указателя (указатель-указатель или объект **) вызывает огромную путаницу.

&size - это адрес переменной, которая содержит размер входного буфера, другого указателя.

stdin - дескриптор входного файла. Таким образом, вы можете использовать getline () для чтения строки текста из файла, но когда указан stdin, читается стандартный ввод.

После чтения строки и сохранения ее в буфере getline возвращает int / ssize_t, равный:

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

OR

2. -1, при ошибке чтения строки (включая условие конца файла).

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

3. Анализируйте вводимые пользователем данные.

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

Возьмем, к примеру, команду:

ls -la /

У нас есть имя двоичного файла (ls) и его аргументы.

команда также может быть:

$  ls       -la      /

Мы собираемся написать функцию, которая сохранит нашу команду (без пробелов) в char **, что даст:

[ls][-la][/]

4. Оболочка проверяет, является ли первый токен (сама основная команда) псевдонимом, и если да, заменяет псевдоним фактической командой.

Оболочка обычно ищет в своих системных файлах определенные псевдонимы. Если команда ls является псевдонимом для чего-то еще, оболочка заменит токен ls строкой для команды, которую представляет ls, чтобы на последующих этапах выполнялась правильная операция.

5. Казнь

Для выполнения нашего заказа мы будем использовать системный вызов execve.

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

Системный вызов fork () используется для создания дочерних процессов в программе на языке C. fork () используется там, где в вашем приложении требуется параллельная обработка. Системная функция fork () определена в заголовках sys / types.h и unistd.h. В программе, в которой вы используете fork, вы также должны использовать системный вызов wait (). Системный вызов wait () используется для ожидания в родительском процессе завершения дочернего процесса. Для завершения дочернего процесса в дочернем процессе используется системный вызов exit (). Функция wait () определена в заголовке sys / wait.h, а функция exit () - в заголовке stdlib.h.

Следующие системные вызовы используются для базового управления процессами.

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

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

exit: Завершает процесс со статусом выхода.

wait: Родитель может использовать ожидание, чтобы приостановить выполнение, пока дочерний элемент не завершится. Используя wait, родитель может получить статус выхода прерванного дочернего элемента.

Вилка возвращается дважды в случае успеха

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

  • Если возвращаемое значение 0, программа выполняется в новом дочернем процессе.
  • Если возвращаемое значение больше нуля, программа выполняется в родительском процессе, а возвращаемое значение - это идентификатор процесса (PID) созданного дочернего процесса.
  • При неудаче fork возвращает -1.

NB: отправляя простую команду, например holberton, в нашу оболочку, execve, мы возвращаем -1 и perror display ./hsh: 3: holberton: not found.

Первый аргумент d'execve должен быть абсолютным путем исполняемого двоичного файла.

Чтобы узнать, где находится программа, нам нужно использовать переменную среды PATH.

Если мы выполним команду:

$> echo $PATH

У нас будет следующий результат:

/bin:/usr/bin:/usr/local/bin

Это файлы (разделенные знаком ":"), иначе наша оболочка будет искать исполняемый двоичный файл.

Теперь нам нужно написать функцию, которая объединит наш путь и двоичный файл.

Вы должны получить содержимое переменной $ PATH с помощью функции getenv. Он принимает единственный параметр, который является переменной, которую мы ищем, и возвращает указатель на содержимое переменной, переданной в качестве параметра.

Если нашего двоичного файла нет в какой-либо папке, мы можем предупредить пользователя одним Command not found, в противном случае мы можем выполнить наш execve: D.

Функция, которая извлекает содержимое переменной $ PATH и возвращает абсолютный путь: D

На этом этапе наша оболочка выполняет команду, но не имеет встроенной программы или среды.

Теперь мы добавим несколько встроенных функций в нашу оболочку.

Встроенная - это команда кодировщика в нашей оболочке. то есть команда, которая не будет выполняться с execve.

Если мы запустим bash и удалим переменную среды PATH, наша оболочка все еще должна иметь возможность выполнять элементарные команды.

команды cd, pwd, exit ...должны быть исполняемыми.

Вы можете перечислить все встроенные функции в bash с помощью команды help

Мы увидим встроенные cd, pwd. Есть много других, но цель состоит в том, чтобы вы понимали, что такое встроенная функция и как ее реализовать.

Для встроенного cd мы будем использовать функциюchdir

chdir принимает в качестве параметра путь, который станет текущей папкой

Пример встроенной реализации cd:

Пора выходить из оболочки

Выход из оболочки может происходить 3 способами:

1- Ввод команды «выход» ИЛИ «выход n» (пока n - целое число)

2- Нажатие Ctrl + D (которое отмечает EOF, конец стандартного входного файла)

3- Нажатие Ctrl + C (что определяется как сигнал для выхода)