Использование строк

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

А что если нам нужно передать аргумент, содержащий символы пробела и переноса строк? Рассмотрим простой пример. Воспользуемся командой cal для вывода календаря.

Результат отображен аккуратно в виде таблички по дням недели, похожей на обычный календарь.

Результат работы команды cal

Теперь подставим результат при помощи развертывания в команду echo, которая выводит каждый переданный ей аргумент.

Результат выглядит не так красиво. Мы потеряли все переносы строк и «лишние» пробелы.

Результат вывода команды cal через echo

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

Рассмотрим второй пример.

Мы потеряли символ валюты и первую цифру нашей «суммы покупки». Здесь сработало развертывание переменных. Но ведь нам же может понадобится вывести символ доллара. Так как же быть? Выход есть.

Различные специальные символы воспринимаются как управляющие символы, а не как часть строки. Например, символ пробела разделяет аргументы, знак доллара ($) используется при развертывании подстановок, перенос строки указывает конец команды.

Экранирование символов

Различные специальные символы воспринимаются как управляющие символы, а не как часть строки. Например, символ пробела разделяет аргументы, знак доллара ($) используется при развертывании подстановок, перенос строки указывает конец команды.

Чтобы использовать специальные символы, их можно экранировать специальным символом «\» (обратная косая черта).

В этот раз в строке все наши пробелы и остались, а символ валюты не был воспринят развертывание переменной. При вводе команды в конце первой строки (перед нажатием Enter) мы поставили символ экранирования, поэтому Linux Shell не стал исполнять команду сразу, а предложил ее продолжить на второй строке. Это очень удобно при написании длинных инструкций (например, команда с множеством аргументов). Мы можем разбить строку на несколько в нужных нам местах, что сделает команду читаемой. В основном данный прием используется при написании Shell скриптов. Очень важно сделать его читаемым и понятным, чтобы в будущем не тратить время пытаясь вспомнить, что мы написали.

Но как же быть с развертыванием? Например, если мы желаем, чтобы результат развертывания был воспринят единым аргументом? И для этого есть простое решение 🙂

Двойные кавычки

Если строку или ее часть поместить в двойные кавычки, то все пробелы и переносы строк внутри будут сохранены.

Как мы видим внутри двойных кавычек работает развертывание строк. Но разворачиваются лишь подстановки, начинающиеся с символа «$» или с обратными одинарными кавычками.

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

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

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

Одинарные кавычки

В строке или части строки, помещенной в одинарной кавычке, полностью подавляется развертывание подстановок. Текст, помещенный в одинарные кавычки остается неизменным.

Одинарные кавычки удобны для записи данных, в которых часто присутствуют специальные символы. Достаточно всю строку обернуть в одинарные кавычки и можно почти ни о чем не беспокоиться 🙂

 

Развертывание строк

Каждый раз когда в командную строку Linux Shell вводится команда для выполнению, bash выполняет множество преобразований над текстом команды перед ее выполнением. Ранее мы сталкивались с подстановками файлов по шаблонам, но детально не разобрали как оно устроено. А оно является частью механизма развертывания. Например, когда в рабочей директории два файла test1.txt и test2.txt, мы можем выбрать их с помощью подстановки по шаблону «t*.txt». Например, если мы желаем их удалить, мы воспользуемся командой rm.

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

Программе rm, как и остальным, нет необходимости в поддержке шаблонов имен файлов, чтобы они работали. Выборка файлов происходит до выполнения программы. А программа выполняет лишь свой функционал Это позволяет экономить значительные ресурсы при разработке программ 🙂

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

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

При помощи команды echo и перенаправления вывода создадим два файла test1.txt и test2.txt с каким нибудь текстом.

С помощью echo можно записывать информацию в файл. Команда echo  считывает выводимые данные из аргументов команды, а не стандартного ввода, как мы это делали ранее с помощью программы cat. Аргументы любой команды являются частью вводимой инструкции, поэтому к ним применимо развертывание. Посмотрим как оно работает при помощи команды echo на подстановках имени файлов по шаблонам.

Каждая подстановка, в которой была произведена выборка файлов, замена последовательностью имен файлов. В последних двух примерах мы выполняли подстановку по шаблону «a*». Данному шаблону не соответствует ни одно имя файла в рабочей директории, потому подстановка развернута не была и осталась в неизменном виде.

Echo и подстановки файлов по шаблонам

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

Пользовательская директория

Нам уже известны специальные ярлыки пользовательских директорий. Они работают на этапе развертывания строк. Символ “~” (тильда) при развертывании заменяется на собственную домашнюю директорию. А если за ним следует имя учетной записи пользователя, тогда вся последовательность, начиная от символа «~» и заканчивая именем пользователя, заменяется на домашнюю директорию данного пользователя.

Арифметические операции

Linux Shell при развертывании вычисляет значения арифметических выражений. Командную строку можно использовать как калькулятор 🙂

Чтобы значение было вычислено, арифметическое выражение необходимо обернуть в двойные круглые скобки и поставить перед ними символ «$» (знак доллара).

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

В данном примере во вложенным арифметическом выражении число 5 возводится в квадрат (2 степень), затем результат умножается на 3.

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

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

Один диск остался в запасе 😉 Как мы заметили, развертывание выражений работает точно также как и развертывание подстановок имени файла по шаблону — происходит замена выражения на результат. Его можно использовать с любыми командами.

Попробуем записать список всех файлов в рабочей директории в файл, имя которого начинается со слова «test», после чего идет число равное результату умножения 25  на 132 (сколько будет? :)) и заканчивается строкой «.txt».

Развертывание последовательностей и комбинации

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

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

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

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

Последовательность можно окружить текстом, тогда при ее разворачивании у элементов последовательности тоже появится окружающий текст.

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

К окружающему тексту тоже применяется развертывание строк.

А значит возможно составление сложных комбинаций их нескольких последовательностей.

Это очень удобно и позволяет сэкономить много времени. Ранее мы пытались создать дерево директорий для сортировки фотографий по датам, которое выглядело как photo/год/месяц/день. Тогда мы создали всего две директории (не считая родительских) photo/2014/01/10 и photo/2010/12/05. Сейчас мы создадим директории для всевозможных дат за 2013-2015 годы. Чтобы это выполнить нам вполне достаточно перечислить их имена (относительные пути), а родительские директории программа mkdir создаст автоматически. Но в году 365 дней, значит нужно перечислить все $((3*365)) 😉 директорий.

Попробуем решить задачу с помощью последовательностей. Первый шаблон, который приходит в голову «photo/{2013..2015}/{01..12}/{01..31}». Но тогда мы получим $((3*12*31)) директорий, что явно больше чем наши $((3*365)) дней в этих годах 🙂 Наверное, нужно учесть, разное количество дней в каждом месяце. Нам поможет вложенная последовательность. В итоге мы получим «photo/{2013..2015}/{{01,03,05,07,08,10,12}/{01..31},{04,06,09,11}/{01..30},02/{01..28}}». Перед тем, как что-то создавать, испытаем наш шаблон при помощи команды echo. Так как результат будет слишком длинным, перенаправим вывод в программу для чтения текста less и пролистаем там.

Начало сгенерированного списка директорий для дат при помощи последовательной и команды echo

Конец сгенерированного списка директорий для дат при помощи последовательной и команды echo

Выглядит как мы и ожидали. Значит теперь создадим все папки, заменив echo на mkdir с ключом -p.

Как видим, все прошло отлично. Столь сложная на первый взгляд задача была решена столько простой командой. Если кто-то не уверен, то может пробежать по всему созданному дереву директорий и проверить его вручную 🙂

Значения переменных

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

Для развертывания значения переменной перед её именем ставится символ «$» (знак доллара). Например, мы желаем узнать значение переменной «USER». Выведем его с помощью команды echo.

Для вывода списка всех переменных окружения и их значений можно воспользоваться командой printenv.

Неустановленная (несуществующая) переменная при развертывании подменяется пустой строкой.

Чтобы задать значение переменной нужно указать имя переменной, после этого поставить символ «=» (знак равно) и указать значение переменной. Между именем переменной и символом «=» не должно быть пробелов. Зададим значение переменной «TEST».

Но, если мы попытаемся вывести список переменных при помощи команды printenv, то нашей переменной «TEST» там не будет.

Если быть более точным, команда printenv выводит только список переменных окружения, а мы задали локальную переменную «TEST». Подробно о внутреннем устройствое переменных и работе с ними я напишу в материалах программирования и разработки скриптов для Linux Shell. Если есть желание увидеть список всех переменных сейчас, можно самостоятельно научиться работать с командой set. Мы ведь помним где получить справку по незнакомой команде? 🙂

Подстановка команд

Мы уже знаем как перенаправить вывод команды в файл или прицепить к вводу другой команды. А еще его можно подставить в строку и передавать как аргументы другой команды. Для развертывания вывода команды, саму команду необходимо обернуть в круглые скобки и поставить перед ними символ “$” (знак доллара). Посмотрим как это работает.

Вместо знака доллара и скобок можно использовать символ «`» (обратная кавычка).

Данный функционал особо полезен при разработке Shell скриптов и его можно использовать совместно с переменными, записывая в них результат выполнения команд. Но об этом позже 🙂

Обзор инструментов обработки информации и фильтров

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

В Linux инструменты обработки информации, которые часто используются в цепочках переадресации ввода-вывода, называются фильтрами. Они считывают информации из стандартного вывода, обрабатывают ее и выводят результат в стандартный вывод. Ввод обычно перенаправляется на вывод предыдущей программы, а вывод либо на вводе следующей программы, либо в файл для сохранения конечного результата. Таким образом, объединив несколько простых инструментов, можно легко выполнить существенное преобразовании информации и получить результат в удобном виде 🙂

Познакомимся с наиболее часто используемыми фильтрами. Более подробно с некоторыми из них мы познакомимся с ними в материалах программирования и разработки скриптов для Linux Shell. Рекомендую самостоятельно ознакомиться со справочной информацией по каждой из описанных программ.

sort
Сортирует строки и выводит их в упорядоченном виде. Мы уже знакомы с этим фильтром 🙂
uniq
Фильтрует повторы соседних строк, выводя каждую строку не более одного раза. Обычно применяется для отсортированного списка через sort, чтобы вырезать все повторы строк из текста.
grep
Просматривает информацию и фильтрует ее по указанному шаблону.
fmt
Считывает текст и выводит его строки в отформатированном виде, удобном для чтения.
pr
Подготавливает информацию к распечатке на бумаге путем разбивки ее на страницы и вставки верхнего и нижнего колонтитула.
head
Выводит только несколько первых строк. Обычно применяется для ознакомления с заголовками файлов.
tail
Выводит только несколько последних строк. Обычно применяется для ознакомления с последними записями журнальных файлов. В составных цепочках используется после фильтрации информации.
tr
Выполняет преобразование символов. Используется для преобразования регистра и типов переноса строк (в различных ОС, концы строк кодируются разными символами).
sed
Редактирует информацию по заданным правилам в процессе ее считывания и сразу выводит результат. Предназначен для более сложных преобразований, которые не может выполнить программа tr. Обработка информации происходит в процессе получения информации без ожидания указателя конца ввода.
awk
Выводит преобразованную информацию по заданным правилам. Достаточно могущественный и сложный инструмент.
wc
Подсчитывает и выводит число строк, слов и символов. Часто употребляется с ключом -l в цепочках, чтобы вывести только одно число — количество строк.

А теперь посмотрим использование некоторых инструментов на примере. Во время путешествия по файлам и директориям Linux мы посещали директорию /var/log, в которой хранятся журнальные файлы. Там хранится файл boot.log, содержащий информацию о последней загрузке системы. Откровенно говоря, я выбрал этот файл для текущего примера только из-за того, что он не требует прав суперпользователя для чтения 😉 Откроем его в программе less, быстро ознакомимся с его содержимым и закроем.

Файл boot.log в less

Заметили странные начала строк? Так less выводит специальные символы, которые не предназначены для прямого отображения на экране (например, перенос страницы). Здесь данные спецсимволы распознаются терминальной программой и используются для выделения слова OK зеленым цветом при выводе в терминале. Но less заменил их на видимые, чтобы отобразить нам их присутствие в тексте. Но сейчас мы это исследовать не будем.

Если приглядеться, то можно заметить, что в файле есть строки, содержащие «Reached target». Они обозначают начала выполнения группы действий. Мы хотим ознакомиться со списком из первых пяти этих групп в алфавитном порядке. Но как это сделать? Искать в less строку «Reached target» мы умеем, но это не очень удобно. Лучше отфильтруем содержимое файла boot.log, оставим в нем лишь строки, которые содержат «Reached target». Отсортируем список по алфавиту и выведем только первые 5 строк.

Цепочка cat, grep, sort, head

С виду сложная задача для решения в графическом интерфейсе была легко решена в командной строке Linux Shell. Теперь, я надеюсь, смысл и удобства цепочек команд понятен всем. А еще мы увидели как выглядит этот таинственно закодированный зеленый цвет 🙂

Сцепление ввода и вывода

А что если нам нужно сам ввод направить в вывод? Например, мы хотим, чтобы то, что мы вводим, сразу же выводилось.

Зачем же нам это? Ну, например, мы ведь можем перенаправить вывод в файл или на принтер, стало быть, если бы у нас была программа, которая выводит то, что мы вводим, мы могли бы легко создавать файлы или печатать на принтере прямо с клавиатуры 🙂

В Linux есть такая программа. Она называется cat. Испытаем ее. После завершения ввода нажмем Ctrl+D (указатель конца ввода).

После того как мы вводим строку, она сразу же выводится обратно. Так в чем же польза? Перенаправим вывод в файл test.txt и попробуем снова, после чего откроем файл в программе просмотра текстовых файлов less.

Содержание файла test.txt после cat

Мы только что научились создавать файлы и записывать в них информацию 🙂

Попробуем в этот же файл добавить еще несколько строчек.

Содержание файла test.txt после cat после добавления строк

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

А теперь попробуем вывести содержание файла test.txt, но не с помощью программы для чтения тестовых файлов less. Сделаем это командой cat. Для этого перенаправим ввод из файла less.txt.

С помощью cat очень удобно просматривать содержание мелких файлов. В отличии от less все содержание файла сразу же выводится и командная строка Linux Shell сразу же ожидает новой инструкции, без необходимости выходить из редактора с помощью кнопки «q».

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

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

Например, мы желаем через программу просмотра текста less открыть содержание файла test.txt, предварительно отсортировав в нем строки по алфавиту. Для этого воспользуемся уже известным нам инструментом sort. Для начала посмотрим как бы это выглядело, если бы мы воспользовались переадресацией ввода из файла. Если быть совсем откровенным, то программа sort по аналогии с cat умеет считывать данные из файла без перенаправления ввода, для этого необходимо лишь указать имена файлов после названия команды. Но сейчас нас интересует не работа программы sort, а принцип построения цепочек конвейерной обработки данных.

А теперь представим, перед sort мы желаем предварительно обработать данные другой программой и только после этого отсортировать результат. Для этого нам бы пришлось перемещать имя файла. Попробуем начать эту же цепочку с команды cat.

Сейчас наше указание выглядит наиболее понятным. Мы просим Linux Shell сначала открыть файл, потом отсортировать, а потом открыть результат в программе для чтения текста less. Если мы пожелаем добавить в цепочку еще какую-либо обработку данных, мы просто вставим ее в нужное месте. Ведь имя файла больше не привязано ни к одной команде обработки данных 🙂