Командный интерфейс
Как было сказано в предыдущей главе, пользовательский интерфейс, который предоставляет операционная система, может быть графическим или командным. Несмотря на все преимущества графического интерфейса, существует множество задач, для которых командный интерфейс существенно удобнее. Более того, поскольку командный интерфейс по уровню абстракции существенно ближе к ядру ОС, чем графический, практика использования командного интерфейса позволяет лучше понять устройство и принципы работы ОС.
Наиболее часто используемый командный интерфейс: интерпретатор команд bash в совокупности с UNIX-инструментами — определённым набором програм различного назначения, первые версии которых появились как неотъемлемая часть ОС UNIX. В состав всех юниксоподобных ОС (кроме iOS) входят UNIX-инструменты (хотя в Android их набор по-умолчанию несколько минималистичен, это нивелируется возможностью установки приложений наподобие Termux или Terminal IDE, содержащих в своём составе более полноценный комплект UNIX-инструментов). Под Windows обычно используются приложения MinGW или Cygwin.
Начнём с описания самой главной части такого командного интерфейса — интерпретатора команд bash.
Интерпретатор bash
Интерпретатор команд (или, как его ещё называют, командная оболочка) — программа, соединённая стандартными потоками ввода/вывода с драйвером терминала, на вход которой пользователь подаёт команды, реализующие ту или иную функциональность ОС. Интерпретатор распознаёт эти команды и вызывает соответствующую функциональность ядра ОС.
Далее будут описаны только основы работы с bash.
Простейшие команды и запуск приложений
Наиболее простая форма команды для bash — просто последовательность слов, разделённых пробелами (количество пробелов между словами несущественно). Словом может являться произвольный текст, хотя некоторые слова нельзя ввести напрямую (например, нельзя напрямую ввести слово, содержащее пробел). Для ввода нетривиальных слов есть три механизма:
- Одинарные кавычки: то, что между ними, воспринимается как одно слово
- Двойные кавычки: некоторые символы и последовательности символов
между ними преобразуются в другие (это преобразование называется интерполяцией);
например, последовательность
\"
преобразуется в двойную кавычку (без обратной косой черты) - Конкатенация: два слова, записанных без пробелов между ними, объединяются;
например
"a\$b"waa'qq'"x"
— это словоa$bwaaqqx
Также важно помнить, что в рамках незакавыченных слов некоторые символы и их последовательности (как и внутри двойных кавычек) имеют специальный смысл, причём зачастую отличный от такового для двойных кавычек.
Последовательность слов bash интерпретирует следующим образом:
- если первое слово является одним из определённого набора зарезервированных слов или же названием одной из встроенных в bash функций, он выполняет действия, определённые семантикой соответствующего слова/функции
- если первое слово не содержит косых черт
/
, он ищет файл с таким названием в рамках одной из определённого набора папок (чем этот набор определяется, будет сказано немного ниже) и (в случае положительного результата поиска) запускает его - если же первое слово содержит
/
, bash считает это слово относительным или абсолютным путём к файлу; в случае наличия такого файла bash запускает его
Под запуском понимается следующее:
- если содержимое файла представляет собой один из форматов, которые ОС считает исполняемыми, bash создаёт потомка, процедура работы которого определяется тем, как ОС интерпретирует этот формат; при этом все слова команды передаются этому потомку при помощи механизма, позволяющего потомку их в случае надобности получить (слова команды называются аргументами командной строки; они нумеруются, начиная с нуля)
- если файл является текстовым и начинается со строчки вида
#!путь_к_файлу
, bash запускает указанный этим путём файл, передавая ему слова команды как аргументы командной строки
Поясним обе эти ситуации на примере. Предположим, что в системе установлен
интерпретатор языка Python, причём его исполняемая часть находится в файле
/usr/bin/python3
. Предположим также, что в текущей директории
находится файл foobar.py
с программой вида
import sys
print(sum(map(int,sys.argv[1:])))
Тогда команда
python3 foobar.py 1 2 3
приведёт к следующему:
- bash найдёт файл
python3
и запустит его, передав ему в качестве аргументов словаpython3
,foobar.py
,1
,2
,3
- файл
python3
представляет собой правильным образом обёрнутый машинный код с программой, интерпретирующей язык программирования Python; эта программа первый аргумент командной строки интерпретирует как название файла с программой на Python - в переменной
sys.argv
(языка Python) хранятся аргументы командной строки, начиная с первого; соответственно, программа вычислит значение выражения1+2+3
и напечатает его
Теперь командой chmod +x foobar.py
добавим хозяину файла (а также заодно —
всем остальным пользователям ОС) foobar.py
право его запускать. Также
допишем в начало этого файла комментарий:
#!/usr/bin/python3
После этого команда
./foobar.py 1 2 3
приведёт к следующему:
- поскольку ОС (скорее всего) откажется воспринимать текстовый файл как
исполняемый, bash посмотрит на его первую строчку, где написано
#!/usr/bin/python3
- bash выполнит команду
/usr/bin/python3 ./foobar.py 1 2 3
- далее всё произойдёт ровно так же, как и в предыдущем случае
Несколько простейших команд
Здесь мы приведём наиболее часто используемые команды (некоторые из них являются встроенными в bash, некоторые — отдельными исполняемыми файлами).
echo
— печатает свои аргументы, разделяя их пробелами, и заканчивая печать переходом на следующую строчкуecho -e
— интерпретирует некоторые экранирующие последовательности в своих аргментах (например,\n
как символ перехода на следующую строчку)echo -n
— не переходит на следующую строчкуecho -ne
илиecho -n -e
— совмещает два вышеописанных пунктаcat
— печатает друг за другом содержимое файлов, названия которых указаны в аргументахpwd
— текущий рабочий каталог для bashls
— его содержимоеls -a
— его содержимое, включая файлы, названия которых начинаются с точкиls -l
— содержимое с кучей подробностей~
— вообще говоря, не команда, а слово, которое, будучи незакавыченным, интерполируется в абсолютный путь к домашней папке пользователя*
— как и тильда, не команда, а слово, которое, будучи незакавыченным, интерполируется в содержимое текущей папкиcd
— смена текущей папки (если запустить без аргументов, то переходит в домашнюю папку)bash
— обычно используется с названием файла в качестве аргумента командной строки; выполняет все команды, записанные в файле, друг за другом в неинтерактивном режимеwhich
— для команд, соответствующих исполняемому файлу, показывает, какому именно файлу команда, поданная аргументом, соответствуетman
— стандартное средство просмотра документации; в качестве аргумента принимает название команды, документация к которой требуетсяinfo
— ещё одно средство просмотра документации; часть документации уinfo
свояless
— интерактивное средство просмотра текстовых файлов; выйти из него можно клавишейq
(ни Ctrl-C, ни Ctrl-D на него не работают)sed
— стандартный текстовый редактор с командным интерфейсом; его описание дано в конце этой главы
Переменные окружения
В bash есть механизм переменных. У каждой переменной есть название — последовательность латинских букв и цифр, начинающаяся с буквы (при этом нижнее подчёркивание также считается буквой). Чтобы определить переменную, нужно написать
название_переменной=слово
Ключевой момент: между названием переменной, знаком =
и словом не должно
быть пробелов!
Далее значение этой переменной можно получить интерполяцией выражения
$название_переменной
. Здесь есть ещё один очень важный момент:
если интерполяция была произведена вне двойных кавычек, значение переменной
разбивается на отдельные слова. То есть, например, последовательность команд
foo="abc def"
cat $foo
печатает содержимое файлов abc
и def
, а не одного файла с названием abc def
.
Для достижения же последнего эффекта нужно дать команду cat "$foo"
.
Важно знать про следующие стандартные переменные:
PATH
— тут хранятся разделённые двоеточиями пути, в которых bash ищет исполняемые файлы0
,1
,2
,3
, и т.д. — аргументы командной строки (существенно, если bash запущен в неинтерактивном режиме)?
— код ошибки последней выполненной команды (равен 0, если команда завершилась без ошибок)
Перенаправление потоков
Когда команда запускается, её стандартные потоки ввода/вывода те же, что и у запустившей её оболочки. Но их можно изменить. Наиболее часто встречаются следующие варианты:
команда < foobar # на вход подаётся содержимое файла foobar
команда > foobar # выход записывается в новый файл foobar
команда >> foobar # выход дописывается в конец файла foobar
Также есть возможность объединить несколько команд в цепочку:
команда_1 | команда_2 | команда_3 | ... | команда_n
В такой цепочке стандартный выход каждой команды (кроме последней) связывается со стандартным входом следующей.
Ещё одно полезное средство — интерполяция стандартного выхода: если
команду взять в обратые кавычки (те, что ставятся клавишей с тильдой),
это выражение преобразуется в тот текст, который взятая в обратные кавычки команда
напечатает. Опять же, как и с интерполяцией переменных, если не взять
такое выражение в двойные кавычки, его значение будет разбито на отдельные слова.
Если хочется интерполировать результат работы нескольких команд, их
можно разделить точкой-с-запятой ;
. Например, так:
echo "`echo a; echo b`"
Полезные возможности
Стрелками вверх/вниз можно перематывать историю команд.
По кнопке Tab происходит автодополнение текущей команды. В современных версиях bash механизм автодополнения довольно продвинутый: он учитывает особенности большинства распространённых приложений.
Потоковый текстовый редактор sed
Текстовый редактор sed представляет собой одно из наиболее примитивных средств для работы с текстом. Принцип его работы очень прост: каждую строчку текста, поданную на стандартный вход, он преобразует по некоторому правилу, после чего печатает результат преобразования в стандартный выход.
Запускается sed командой:
sed -e правило
Правило представляет собой последовательность фильтров, разделённых точками-с-запятой. Каждый фильтр представляет собой предикат, за которым идёт простое или сложное преобразование (сложное преобразование оформляется как последовательность простых, разделённых точками-с-запятой, взятая в фигурные скобки).
Обычно используются следующие предикаты:
- пустой — ему удовлетворяет любая строчка
- доллар
$
— ему удовлетворяет последняя строчка - число — ему удовлетворяет строчка с таким номером (нумерация начиается с единицы)
- два числа, разделённых запятой — ему удовлетворяют все строчки в указанном диапазоне (включая концы)
- образец, заключённый в обратные косые черты — ему удовлетворяют все строчки, соответствующие образцу
- восклицательный знак, поставленный после предиката, означает отрицание к нему (например,
2,4!
— все строчки, кроме второй, третьей и четвёртой)
Наиболее полезные команды такие:
s/образец/результат/
— заменяет часть строчки, удовлетворяющую образцу, на результатq
— печатает результат преобразований к данному моменту и завершает работуd
— вообще не печатает результат преобразования и сразу переходит к обработке следующей строчкиp
— печатает результат преобразований к данному моментуn
— сразу переходит к следующей строчке, но при этом продолжает текущую последовательность преобразований
Если запустить sed комнадной sed -ne
, то q
и n
перестают что-либо
печатать, а также отключается печать по-умолчанию. Например, чтобы напечатать
строчки с 10-й по 20-ю, проще написать
sed -ne '10,20p'
чем
sed -e '1,9d;21,$d'
Синтаксис образцов
Образцы используются для описания предикатов над текстами.
Самый простой образец имеет вид текста, который ему удовлетворяет
(символы $
, ^
, *
, [
, \
, .
нужно экранировать при помощи \
перед ними). Также такому образцу удовлетворяет текст, содержащий
этот образец в качестве подтекста. Например, образцу foo
удовлетворяют
тексты foo
, foobar
, barfoo
и barfoobaz
(а также —
бесконечное множество других).
Ещё в образец разрешается включать одно из (это не всё, что имеется, но наиболее полезное):
- образцу
.
удовлетворяет любой один символ - образцу
[...]
удовлетворяет любая буква, указанная внутри квадратных скобок - образцу
[^...]
удовлетворяет любая буква, не указанная внутри квадратных скобок - образцу
^
удовлетворяет начало строчки - образцу
$
удовлетворяет конец строчки
Например, образцу ^[A-Z][a-z][a-z]
удовлетворяет текст, состоящий хотя бы
из трёх букв, первая буква которого — заглавная латинская, а вторая и
третья — строчные латинские.
Часть образца можно заключить в скобки \(
и \)
; тот кусок текста, который
удовлетворяет образцу в скобках, запоминается под некоторым номером от 1 до 9
(куски нумеруются по порядку, слева направо); запомненный кусок можно использовать
либо в рамках того же образца, либо в правой части команды s
при помощи выражения
\1
, \2
, \3
и т.д.
Справа от односимвольного образца или же образца, заключённого в скобки,
можно поставить квантор: выражение \{M,N\}
или \{M,\}
, где M
и N
— целые
неотрицательные числа. Такому образцу удовлетворяет текст, состоящий из
K
текстов, каждый из которых удовлетворяет образцу перед квантором, где
K
не меньше M
и не больше N
. Например, образцу ^[ab]\{1,2\}$
удовлетворяют только тексты a
, b
, aa
, ab
, ba
, bb
.
Для некоторых кванторов есть сокращения:
*
— сокращение для\{0,\}
(хотя бы ноль)\+
— сокращение для\{1,\}
(хотя бы один)\?
— сокращение для\{0,1}
(ноль или один)
В заключение отметим, что при использовании образца
внутри команды s
выбирается та часть текста, удовлетворяющая образцу,
для которой:
- её начало самое левое среди всех таких частей
- каждый квантор соответствует наибольшему возможному количеству повторений
при фиксированных количествах повторений кванторов слева от него
(например образцу
\(ab\)*\(aba\)*
в текстеababa
удовлетворяет частьabab
с количествами повторений 2 и 0, а не частьababa
с количествами повторений 1 и 1).
Если нужно выбрать все части, удовлетворяющие образцу (точнее, максимальный
непересекающийся набор таких частей), можно в конце команды s
поставить g
:
sed -e 's/foo/bar/' # в каждой строчке заменяет самое первое слово foo на bar
sed -e 's/foo/bar/g' # в каждой строчке заменяет все слова foo на bar
Примеры
Здесь мы приведём часто используемые правила для sed
.
s/^/foobar/
— дописываетfoobar
в начало строчкиs/$/foobar/
— дописываетfoobar
в конец строчкиs/^.*$/foobar/
— заменяет всю строчку наfoobar
/^$/d
— удаляет пустую строчку$d
— удаляет последнюю строчку