Перенаправление ввода вывода Linux. Текстовые потоки и фильтры

Одна из самых интересных и полезных тем для системных администраторов и новых пользователей, которые только начинают разбираться в работе с терминалом - это перенаправление потоков ввода вывода Linux. Эта особенность терминала позволяет перенаправлять вывод команд в файл, или содержимое файла на ввод команды, объединять команды вместе, и образовать конвейеры команд.

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

Все команды, которые мы выполняем, возвращают нам три вида данных:

  • Результат выполнения команды, обычно текстовые данные, которые запросил пользователь;
  • Сообщения об ошибках - информируют о процессе выполнения команды и возникших непредвиденных обстоятельствах;
  • Код возврата - число, которое позволяет оценить правильно ли отработала программа.

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

  • STDIN или 0 - этот файл связан с клавиатурой и большинство команд получают данные для работы отсюда;
  • STDOUT или 1 - это стандартный вывод, сюда программа отправляет все результаты своей работы. Он связан с экраном, или если быть точным, то с терминалом, в котором выполняется программа;
  • STDERR или 2 - все сообщения об ошибках выводятся в этот файл.

Перенаправление ввода / вывода позволяет заменить один из этих файлов на свой. Например, вы можете заставить программу читать данные из файла в файловой системе, а не клавиатуры, также можете выводить ошибки в файл, а не на экран и т д. Все это делается с помощью символов "<" и ">" .

Перенаправить вывод в файл

Все очень просто. Вы можете перенаправить вывод в файл с помощью символа >. Например, сохраним вывод команды top:

top -bn 5 > top.log

Опция -b заставляет программу работать в не интерактивном пакетном режиме, а n - повторяет операцию пять раз, чтобы получить информацию обо всех процессах. Теперь смотрим что получилось с помощью cat:

Символ ">" перезаписывает информацию из файла, если там уже что-то есть. Для добавления данных в конец используйте ">>" . Например, перенаправить вывод в файл linux еще для top:

top -bn 5 >> top.log

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

top -bn 5 1>top.log

Перенаправить ошибки в файл

Чтобы перенаправить вывод ошибок в файл вам нужно явно указать дескриптор файла, который собираетесь перенаправлять. Для ошибок - это номер 2. Например, при попытке получения доступа к каталогу суперпользователя ls выдаст ошибку:

Вы можете перенаправить стандартный поток ошибок в файл так:

ls -l /root/ 2> ls-error.log
$ cat ls-error.log

Чтобы добавить данные в конец файла используйте тот же символ:

ls -l /root/ 2>>ls-error.log

Перенаправить стандартный вывод и ошибки в файл

Вы также можете перенаправить весь вывод, ошибки и стандартный поток вывода в один файл. Для этого есть два способа. Первый из них, более старый, состоит в том, чтобы передать оба дескриптора:

ls -l /root/ >ls-error.log 2>&1

Сначала будет отправлен вывод команды ls в файл ls-error.log c помощью первого символа перенаправления. Дальше в тот же самый файл будут направлены все ошибки. Второй метод проще:

ls -l /root/ &> ls-error.log

Также можно использовать добавление вместо перезаписи:

ls -l /root/ &>> ls-error.log

Стандартный ввод из файла

Большинство программ, кроме сервисов, получают данные для своей работы через стандартный ввод. По умолчанию стандартный ввод ожидает данных от клавиатуры. Но вы можете заставить программу читать данные из файла с помощью оператора "<" :

cat

Вы также можете сразу же перенаправить вывод тоже в файл. Например, пересортируем список:

sort sort.output

Таким образом, мы в одной команде перенаправляем ввод вывод linux.

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

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

ls -lt | head -n 5

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

echo test/ tmp/ | xargs -n 1 cp -v testfile.sh

Здесь параметр -n 1 задает, что для одной команды нужно подставлять только один параметр, а опция -v в cp позволяет выводить подробную информацию о перемещениях. Еще одна, полезная в таких случаях команда - это tee. Она читает данные из стандартного ввода и записывает в стандартный вывод или файлы. Например:

echo "Тест работы tee" | tee file1

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

Выводы

В этой статье мы рассмотрели основы перенаправления потоков ввода вывода Linux. Теперь вы знаете как перенаправить вывод в файл linux или вывод из файла. Это очень просто и удобно. Если у вас остались вопросы, спрашивайте в комментариях!

Любая программа - это "автомат", предназначенный для обработки данных: получая на входе одну информацию, она в результате работы выдает другую. Хотя входящая и/или выходящая информация может быть и нулевой, т. е. попросту отсутствовать. Те данные, которые передаются программе для обработки - это ее ввод, то, что она выдает в результате работы - вывод. Организация ввода и вывода для каждой программы - это задача операционной системы.

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

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

Рассмотрим сначала пару команд, с помощью которых можно организовать ввод/вывод.

Команды вывода на стандартное устройство вывода

Linux предоставляет несколько команд для вывода сообщений в стандартный поток вывода:

  • echo - Вывести строку в стандартный поток вывода.
  • printf - Вывести форматированный текст в стандартный поток вывода.
  • yes - Выводить повторяющийся текст в стандартный поток вывода.
  • seq - Вывести последовательность чисел в стандартный поток вывода
  • clear Очистить экран или окно.

Например, при использовании команды echo если указать управляющий символ \с, то по завершении вывода не будет осуществлен переход в новую строку:

$ echo "Как вас зовут?\c"

Как вас зовут?$

Здесь $ – символ приглашения.

В строке также можно вычислять значения переменных интерпретатора shell и даже других команд. Например, следующая команда сообщает о том, каков начальный каталог текущего пользователя (переменная среды $HOME) и к какому терминалу он подключен (команда tty заключена в обратные кавычки, чтобы интерпретатор поместил в строку результат ее выполнения).

$ echo "Ваш начальный каталог - $HOME, вы подключены к терминалу - `tty` "

Ваш начальный каталог - /home/knoppix, вы подключены к терминалу - /dev/tty1

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

Например, чтобы вывести строку “/dev/tty1” необходимо выполнить:

$echo “\”/dev/tty1\””

Команды ввода из стандартного устройства ввода

Команда read читает одну строку из стандартного входного потока и записывает ее содержимое в указанные переменные. При указании нескольких переменных в первую из них записывается первое слово, во вторую – второе и т.д. в последнюю – остаток строки.

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


$ cat test
#!/bin/bash
echo “Имя: \с”
read name
echo “Фамилия: \c”
read surname
echo “Имя=” $name “Фамилия=” $surname

Тогда для выполнения этого сценария необходимо файлу test дать право выполнения: chmod 0755 test и запустить его./test. Результат выполнения: Имя: Иван Фамилия: Петров Имя=Иван Фамилия=Петров

СТАНДАРТНЫЕ ПОТОКИ ВВОДА, ВЫВОДА И ОШИБОК

Каждая запущенная из командного интерпретатора программа получает три открытых потока ввода/вывода:

Стандартный ввод (sldin) - стандартный вывод(sldout) - стандартный вывод ошибок (stderr)

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

При этом с каждым процессом (командой, сценарием и т.п.), выполняемым в интерпретаторе shell, связан рад открытых файлов, из которых процесс может читать свои данные: и в которые он может записывать их. Каждый из этих файлов идентифицируется числом, называемым дескриптором файла, но первые три файла являются потоками ввода/вывода по умолчанию:

Файл Дескриптор
Стандартный поток ввода 0
Стандартный поток вывода 1
Стандартный поток ошибок 2

В действительности создается 12 открытых файлов, но файлы с дескрипторами 0, 1 и 2 резервируются для стандартных потоков ввода, вывода и ошибок. Пользователи могут также работать с файлами, имеющими дескрипторы от 3 до 9 (зарезервированы).

Файл стандартного потока ввода (sldin) имеет дескриптор 0. Из этого файла процессы извлекают свои входные данные. По умолчанию входной поток ассоциирован с клавиатурой (устройство /dev/tty), но чаше всего он поступает по каналу от других процессов или из обычного файла.

Файл стандартного потока вывода (stdout) имеет дескриптор 1. В этот файл записываются все выходные данные процесса. По умолчанию данные выводятся на экран терминала (устройство/dev/tty), но их можно также перенаправить в файл или послать по каналу другому процессу.

Файл стандартного потока ошибок (siderr) имеет дескриптор 2. В этот файл записываются сообщения об ошибках, возникающих в ходе выполнения команды. По умолчанию сообщения об ошибках выводятся на экран терминала (устройство /dev/tty), но их также можно перенаправить в файл. Зачем же для регистрации ошибок выделять специальный файл? Дело в том, что это очень удобный способ выделения из результатов работы команды собственно выходных данных, а также хорошая возможность эффективно организовать ведение различного рода журнальных файлов.

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

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

Полезный частный случай использования механизма перенаправления потоков - перенаправление в /dev/null, что позволяет избавиться от ненужных сообщений на экран. С помощью того же механизма можно создавать пустые файлы:

% cat myfile - создаст в текущей директории пустой файл myfile.

/dev/null - специальный файл, представляющий собой т. н. «пустое устройство». Запись в него происходит успешно, независимо от объёма «записанной» информации. Чтение из /dev/null эквивалентно считыванию конца файла EOF.

Перенаправление потоков ввода-вывода осуществляется, подобно DOS (Точнее, синтаксис перенаправления потоков ОС DOS восприняла от UNIX) с помощью символов:

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

Однако, в отличие от DOS при создании программного канала между двумя процессами ОС UNIX/Linux запускает оба процесса одновременно и осуществляет передачу информации через системный буфер (без промежуточной записи на жесткий диск). Таким образом, программные каналы в ОС UNIX/Linux являются весьма эффективным способом обмена. В случае переполнения системного буфера (например если ``передающая"" программа выдает информацию в канал быстрее чем ее может обработать ``принимающая"" программа) ОС автоматически приостанавливает тот процесс, который осуществляет запись в канал до освобождения буфера.

Наиболее распространенные операторы переадресации

№п/п Синтаксис Описание
1 команда > файл Направляет стандартный поток вывода в новый файл

2 команда 1> файл Направляет стандартный поток вывода в указанный файл

3 команда >> файл Направляет стандартный поток вывода в указанный файл (режим присоединения)

4 команда > файл 2>&1 Направляет стандартные потоки вывода и ошибок в указанный файл

5 команда 2> файл Направляет стандартный поток ошибок в указанный файл

6 команда 2>> файл Направляет стандартный поток ошибок в указанный файл (режим присоединения)

7 команда >> файл 2>&1 Направляет стандартные потоки вывода и ошибок в указанный файл (режим присоединения)

8 команда < файл1 > файл2 Получает входные данные из первого файла и направляет выходные данные во второй файл

9 команда < файл в качестве стандартного входного потока получает данные из указанного файла

10 команда << разделитель Получает данные из стандартного потока ввода до тех пор, пока не встретится разделитель

11 команда <&m В качестве стандартного входного потока получает дан­ные из файла с дескриптором m

12 команда >&m Направляет стандартный поток вывода в файл с дескриптором m

Оператор n>&m позволяет перенаправить файл с дескриптором n туда, куда направлен файл с дескриптором m. Подобных операторов в командной строке может быть несколько, в этом случае они вычисляются слева направо.

Команда exec и применение дескрипторов файлов

Команда exec заменяет текущий интерпретатор shell указанной командой. Обычно она используется для того, чтобы закрыть текущий интерпретатор и запустить другой. Но у нее есть и другое применение.

Например, команда вида

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

Exec <&– которая закрывает стандартный входной поток (в данном случае файл). Подобный прием применяется
преимущественно в сценариях, выполняющихся при выходе из системы.

Команда exec указатель на файл с дескриптором 0 (stdin). Восстановить этот указатель можно будет только по завершении работы сценария.
Если же в сценарии предполагается продолжить чтение данных с клавиатуры, то необходимо сохранить
указатель на прежний входной поток. Ниже приведен небольшой сценарий, в котором демонстрируется, как это сделать.

$ cat f_desc
#!/bin/bash
exec 3<&0 0<file
read linel
read line2
exec 0<&3
echo $1inel
echo $line2

Первая команда exec сохраняет указатель на стандартный входной поток (stdin) в файле с дескриптором 3
(допускается любое целое число в диапазоне от 3 до 9), а затем открывает файл file для чтения. Следующие две команды read
читают из файла две строки текста. Вторая команда exec восстанавливает указатель на стандартный входной поток: теперь
он связан с файлом stdin, а не file. Завершающие команды echo отображают на экране содержимое прочитанных строк,
которые были сохранены в переменных linel и Iine2.

Результат работы сценария:
$ ./ f_desc
Привет!
Пока!

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

5.5.1 Операторы >, < и >>

Для обозначения перенаправления используются символы "> ", "< " и ">> ". Чаще всего используется перенаправление вывода команды в файл. Вот соответствующий пример:

$ ls -l > /home/jim/dir.txt

По этой команде в файле /home/jim/dir.txt будет сохранен перечень файлов и подкаталогов того каталога, который был текущим на момент выполнения команды ls ; при этом если указанного файла не существовало, то он будет создан; если он существовал, то будет перезаписан; если же вы хотите, чтобы вывод команды был дописан в конец существующего файла, то надо вместо символа > использовать >> . При этом наличие пробелов до или после символов > или >> несущественно и служит только для удобства пользователя.

Вы можете направить вывод не только в файл, но и на вход другой команды или на устройство (например, принтер). Так, для подсчета числа слов в файле /home/jim/report.txt можно использовать следующую команду:

$ cat /home/jim/report.txt > wc -w

а для вывода файла на печать — команду:

$ cat /home/jim/report.txt > lpr

Как видите, оператор > служит для перенаправления выходного потока. По отношению к входному потоку аналогичную функцию выполняет оператор < . Приведенный выше пример команды для подсчета числа слов в определенном файле можно переписать следующим образом (обратите внимание на отсутствие команды cat ):

$ wc -w < /home/jim/report.txt

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

В силу того, что символы < , > и >> действуют на стандартные потоки, их можно использовать не только тем привычным образом, как это делается обычно, но и несколько по-другому. Так, следующие команды эквивалентны:

$ cat > file

$ cat>file

$ >file cat

$ > file cat

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

$ file1 > file2

получить копию какого-то файла. Но это не уменьшает значения данного механизма, ведь стандартные потоки определены для любой команды. При этом перенаправить можно не только стандартный ввод и вывод, но и другие потоки. Для этого надо указать перед символом перенаправления номер перенаправляемого потока. Стандартный ввод stdin имеет номер 0, стандартный вывод stdout — номер 1, стандартный поток сообщений об ошибках stderr — номер 2. То есть полный формат команды перенаправления имеет вид (напомним, что пробелы возле > не обязательны):

command N > M

где N и M — номера стандартных потоков (0,1,2) или имена файлов. Употребление в некоторых случаях символов < , > и>> без указания номера канала или имени файла возможно только потому, что вместо отсутствующего номера по умолчанию подставляется 1, т. е. стандартный вывод. Так, оператор > без указания номера интерпретируется как 1 > .

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

command N > &M

Такая команда означает, что выход канала с номером N направляется как на стандартный вывод, так и дублируется в канал с номером M . Например, для того, чтобы сообщения об ошибках дублировались на стандартный вывод, надо дать команду 2>&1, в то время как 1>&2 дублирует stdout в stderr. Такая возможность особенно полезна при перенаправлении вывода в файл, так как мы тогда одновременно и видим сообщения на экране, и сохраняем их в файле.

5.5.2 Оператор |

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

$ cat myfile | grep Linux | wc -l

Эта строка означает, что вывод команды cat , т. е. текст из файла myfile, будет направлен на вход команды grep , которая выделит только строки, содержащие слово "Linux". Вывод команды grep будет, в свою очередь, направлен на вход команды wc -l , которая подсчитает число таких строк.

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

Надо отметить, что оболочка одновременно вызывает на выполнение все команды, включенные в конвейер, запуская для каждой из команд отдельный экземпляр оболочки, так что как только первая программа начинает что-либо выдавать в свой выходной поток, следующая команда начинает его обрабатывать. Точно так же каждая следующая команда выполняет свою операцию, ожидая данных от предыдущей команды и выдавая свои результаты на вход последующей. Если вы хотите, чтобы какая-то команда полностью завершилась до начала выполнения последующей, вы можете использовать в одной строке как символ конвейера | , так и точку с запятой ; . Перед каждой точкой с запятой оболочка будет останавливаться и ожидать, пока завершится выполнение всех предыдущих команд, включенных в конвейер.

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

5.5.3 Фильтры

Последний из приведенных выше примеров (с командой grep ) можно использовать для иллюстрации еще одного важного понятия, а именно, программы-фильтра. Фильтры — это команды (или программы), которые воспринимают входной поток данных, производят над ним некоторые преобразования и выдают результат на стандартный вывод (откуда его можно перенаправить куда-то еще по желанию пользователя). К числу команд-фильтров относятся уже упоминавшиеся выше команды cat, more, less, wc, cmp, diff , а также следующие команды.

Таблица 5.1. Команды-фильтры

Команда

Краткое описание

grep , fgrep , egrep

Ищут во входном файле или данных со стандартного ввода строки, содержащие указанный шаблон, и выдают их на стандартный вывод

Заменяет во входном потоке все встречающиеся символы, перечисленные в заданном перечне, на соответствующие символы из второго заданного перечня

comm

Сравнивает два файла по строкам и выдает на стандартный вывод 3 колонки: в одной — строки, которые встречаются только в 1 файле, во второй — строки, которые встречаются только во 2-ом файле: и в третьей — строки, имеющиеся в обоих файлах

Форматирует для печати текстовый файл или содержимое стандартного ввода

sed

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

Особым фильтром является команда tee , которая "раздваивает" входной поток, с одной стороны направляя его на стандартный вывод, а с другой — в файл (имя которого вы должны задать). Легко видеть, что по своему действию команда tee аналогична оператору перенаправления 1>&file .

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

О перенаправлении и фильтрах можно было бы говорить очень много. Но этот материал имеется в большинстве книг по UNIX и Linux, например у Петерсена [П1.4] и Келли-Бутла [П1.8] . Поэтому ограничимся сказанным, и перейдем к рассмотрению так называемой среды или окружения, создаваемого оболочкой.

В. Костромин (kos at rus-linux dot net) - 5.5. Перенаправление ввода/вывода, каналы и фильтры

Изучаем Linux, 101

Текстовые потоки и фильтры

Обработка текста в командной строке с использованием текстовых утилит GNU

Серия контента:

Краткий обзор

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

В этой статье будут рассмотрены следующие темы:

Об этой серии

Эта серия статей поможет вам освоить задачи администрирования операционной системы Linux. Вы также можете использовать материал этих статей для подготовки к .

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

  • Обработка текстовых файлов и потоков вывода текстовыми фильтрами для модификации вывода.
  • Использование стандартных команд UNIX из состава пакета GNU textutils.
  • Использование редактора sed для создания сценариев, состоящих из сложных последовательностей действий над текстовыми файлами.

Эта статья поможет вам подготовиться к сдаче экзамена LPI 101 на администратора начального уровня (LPIC-1) и содержит материалы цели 103.2 темы 103. Цель имеет вес 3. Материал этой статьи соответствует целям экзамена LPI 101 по состоянию на апрель 2009 года. Всегда обращайтесь к Web-сайту программы сертификации LPIC, чтобы уточнить последние цели.

Необходимые условия

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

Фильтрация текста

Как связаться с Яном

Ян – один из наших наиболее популярных и плодовитых авторов. Ознакомьтесь со (EN), опубликованными на сайте developerWorks. Вы можете найти контактные данные в и связаться с ним, а также с другими авторами и участниками ресурса My developerWorks.

Фильтрация текста – это процесс получения входного текстового потока, выполнения неких преобразований над ним и передача измененных данных в выходной поток. Хотя входные или выходные данные могут поступать из файлов, в UNIX® и Linux фильтрация обычно осуществляется путем составления конвейеров из команд, в которых вывод одной команды передается по программному каналу (или перенаправляется ) на вход следующей команде. Более подробно программные каналы и перенаправления рассматриваются в статье "Потоки, программные каналы и перенаправления " (см. ), а сейчас давайте рассмотрим программные каналы и простые перенаправления вывода с помощью операторов | и >.

Потоки

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

  • stdin стандартный поток ввода (standard input stream), обеспечивающий ввод для команд.
  • stdout стандартный поток вывода (standard output stream), обеспечивающий отображение результатов выполнения команд.
  • stderr стандартный поток ошибок (standard error stream), обеспечивающий отображение ошибок, возникающих при выполнении команд.

Конвейеризация с использованием оператора |

Передаваемые командам параметры могут служить входными данными этих команд, а выходные данные могут выводиться на ваш терминал. Многие команды обработки текста (фильтры) могут получать входные данные либо из стандартного потока ввода, либо из файла. Чтобы передать вывод команды1 на вход команде2 (выступающей в качестве фильтра), необходимо соединить эти две команды оператором конвейеризации ввода/вывода (|). В листинге 1 показано, как перенаправить вывод команды echo на вход команде sort , которая сортирует полученный список слов.

Листинг 1. Передача вывода команды echo на вход команды sort
$ echo -e "apple\npear\nbanana"|sort apple banana pear

У любой из этих команд могут иметься опции или аргументы. С помощью оператора | можно также перенаправить вывод второй команды на вход третьей команде и так далее. Построение длинных конвейеров из команд, каждая из которых имеет свой ограниченный функционал – это распространенный в Linux и UNIX прием, используемый для решения поставленных задач. Иногда аргументом команды может являться не имя файла, а знак дефиса (-); это означает, что входные данные следует принимать со стандартного устройства ввода, а не из файла.

Перенаправление вывода с помощью оператора >

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

В оставшихся примерах этой статьи мы будем использовать небольшие файлы, поэтому давайте создадим директорию с именем lpi103-2 и перейдем в нее. После этого давайте перенаправим с помощью оператора > вывод команды echo в файл с именем text1. Эти действия показаны в листинге 2. Заметьте, что поскольку весь вывод перенаправляется в файл, он не отображается на экране.

Листинг 2. Перенаправление вывода команды в файл
$ mkdir lpi103-2 $ cd lpi103-2 $ echo -e "1 apple\n2 pear\n3 banana" > text1

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

Команды cat, od и split

После того, как вы создали файл test1, вы можете просмотреть его содержимое. Для вывода содержимого файла на стандартное устройство вывода используется команда cat (сокращенно от concatenate – объединять). В листинге 3 на экран выводится содержимое только что созданного нами файла.

Листинг 3. Вывод содержимого файла с помощью команды cat
$ cat text1 1 apple 2 pear 3 banana

Если не указать имя файла (или поставить вместо имени файла дефис), то команда cat принимает входные данные со стандартного устройства ввода. Давайте используем эту возможность (а также перенаправление вывода) для создания еще одного текстового файла, как показано в листинге 4.

Листинг 4. Создание текстового файла с помощью команды cat
$ cat >text2 9 plum 3 banana 10 apple
Другие простые фильтры

Другим примером простого фильтра является команда tac (перевернутое имя команды cat). Эта команда выполняет действие команды cat наоборот – строки файла выводятся в обратном порядке. Попробуйте самостоятельно выполнить следующую команду:
tac text2 text1

В листинге 4 команда cat продолжает считывать данные с устройства stdin до тех пор, пока не будет достигнут конец файла. Чтобы обозначить конец файла, нажмите комбинацию клавиш Ctrl-d (удерживайте Ctrl и нажмите d ). Эта же комбинация клавиш используется для выхода из командного интерпретатора bash. Используйте клавишу табуляции, чтобы выстроить названия фруктов в столбец.

Вы еще не забыли, что cat – это сокращение от concatenate ? С помощью cat вы можете объединить несколько файлов и вывести их содержимое на экран. В листинге 5 показано содержимое двух файлов, которые мы создали.

Листинг 5. Объединение двух файлов с помощью команды cat
$ cat text* 1 apple 2 pear 3 banana 9 plum 3 banana 10 apple

Обратите внимание на различное выравнивание содержимого двух текстовых файлов при выводе их на экран с помощью cat . Чтобы разобраться, почему это происходит, необходимо посмотреть на управляющие символы, которые присутствуют в файлах. Эти символы влияют на вывод текста, но не имеют визуального отображения, поэтому необходимо создать дамп файла в формате, позволяющем увидеть и определить эти специальные символы. Для этих целей предназначена GNU-утилита od (O ctal D ump).

Команда od имеет несколько опций; например, опция -A управляет основанием смещения файлов, а опция -t – формой выводимого содержимого. Основание может быть указано как o (восьмеричное, используется по умолчанию), d (десятичное), x (шестнадцатеричное) или n (смещения не отображаются). Вы можете выводить содержимое в виде восьмеричных, шестнадцатеричных, десятичных значений, значений с плавающей точкой, ASCII-символов с escape-последовательностями или именованных символов (nl – для новой строки, ht – для горизонтальной табуляции и т. д.). В листинге 6 показано несколько доступных форматов дампа файла text2 из нашего примера.

Листинг 6. Дампы файлов, созданные с помощью команды od
$ od text2 0000000 004471 066160 066565 031412 061011 067141 067141 005141 0000020 030061 060411 070160 062554 000012 0000031 $ od -A d -t c text2 0000000 9 \t p l u m \n 3 \t b a n a n a \n 0000016 1 0 \t a p p l e \n 0000025 $ od -A n -t a text2 9 ht p l u m nl 3 ht b a n a n a nl 1 0 ht a p p l e nl

Примечания:

Файлы, используемые в наших примерах, очень малы, но иногда вам могут встретиться большие файлы, которые необходимо разделить на несколько более мелких. Например, вам может потребоваться разбить один большой файл на несколько частей такого размера, чтобы их можно было записать на компакт-диски. Для этого можно использовать команду split , которая разбивает файлы таким образом, что впоследствии их можно легко собрать обратно в единый файл при помощи команды cat . По умолчанию имена файлов, создаваемых командой split , состоят из префикса "x", за которым следует суффикс "aa", "ab", "ac", ..., "ba", "bb" и так далее. Эти умолчания можно изменить с помощью различных опций. Вы также можете задавать размер выходных файлов и определять, будут ли они содержать определенное количество строк или просто иметь определенный размер в байтах.

В листинге 7 показано разделение наших двух текстовых файлов с использованием различных префиксов для выходных файлов. Файл text1 мы разделили на файлы, содержащие максимум две строки, а файл text2 – на файлы максимальным размером в 18 байтов. Далее с помощью команды cat мы отобразили некоторые отдельные части, а также весь файл целиком, используя универсализацию файловых имен , которая рассматривается в статье "Основы управления файлами и директориями " (см. ).

Листинг 7. Разделение и восстановление файлов с помощью команд split и cat
$ split -l 2 text1 $ split -b 17 text2 y $ cat yaa 9 plum 3 banana 1$ cat yab 0 apple $ cat y* x* 9 plum 3 banana 10 apple 1 apple 2 pear 3 banana

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

Команды wc, head и tail

Команда cat выводит полное содержимое файла. Это хорошо подходит для небольших файлов (например, для наших примеров), но что делать, если объем файла очень большой? Итак, для начала можно оценить размер файла с помощью команды wc (W ord C ount – подсчет слов). Команда wc выводит количество содержащихся в файле строк и слов, а также размер файла в байтах, определить который можно также с помощью команды ls -l . В листинге 8 показан подробный вывод сведений о наших двух текстовых файлах, а также вывод команды wc .

Листинг 8. Использование команды wc для работы с текстовыми файлами
$ ls -l text* -rw-rw-r--. 1 ian ian 24 2009-08-11 14:02 text1 -rw-rw-r--. 1 ian ian 25 2009-08-11 14:27 text2 $ wc text* 3 6 24 text1 3 6 25 text2 6 12 49 total

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

Две другие команды позволяют отображать либо первую часть файла (заголовок ), либо последнюю (хвост ). Эти команды так и называются – head и tail соответственно. Их можно использовать в качестве фильтров или передавать им в качестве аргумента имя файла. По умолчанию эти команды отображают 10 первых (или последних) строк файла или потока. В листинге 9 совместно используются команды dmesg (отображение информации о загрузке системы), wc , tail и head ; в результате мы видим, что файл содержит 791 сообщение, выводим последние 10 из них, а затем выводим шесть сообщений, начиная с 15 от конца.

Листинг 9. Использование команд wc, head и tail для вывода сообщений о загрузке
$ dmesg|wc 791 5554 40186 $ dmesg | tail input: HID 04b3:310b as /devices/pci0000:00/0000:00:1a.0/usb3/3-2/3-2.4/3-2.4:1.0/input/i nput12 generic-usb 0003:04B3:310B.0009: input,hidraw1: USB HID v1.00 Mouse on us b-0000:00:1a.0-2.4/input0 usb 3-2.4: USB disconnect, address 11 usb 3-2.4: new low speed USB device using uhci_hcd and address 12 usb 3-2.4: New USB device found, idVendor=04b3, idProduct=310b usb 3-2.4: New USB device strings: Mfr=0, Product=0, SerialNumber=0 usb 3-2.4: configuration #1 chosen from 1 choice input: HID 04b3:310b as /devices/pci0000:00/0000:00:1a.0/usb3/3-2/3-2.4/3-2.4:1.0/input/i nput13 generic-usb 0003:04B3:310B.000A: input,hidraw1: USB HID v1.00 Mouse on us b-0000:00:1a.0-2.4/input0 usb 3-2.4: USB disconnect, address 12 $ dmesg | tail -n15 | head -n 6 usb 3-2.4: USB disconnect, address 10 usb 3-2.4: new low speed USB device using uhci_hcd and address 11 usb 3-2.4: New USB device found, idVendor=04b3, idProduct=310b usb 3-2.4: New USB device strings: Mfr=0, Product=0, SerialNumber=0 usb 3-2.4: configuration #1 chosen from 1 choice input: HID 04b3:310b as /devices/pci0000:00/0000:00:1a.0/usb3/3-2/3-2.4/3-2.4:1.0/input/i nput12

Другим распространенным применением команды tail является слежение за файлом; для этого используется опция -f и шаг, обычно равный одной строке. Это может оказаться полезным в том случае, если у вас имеется фоновый процесс, генерирующий вывод данных в файл, и вы хотите следить за ходом его выполнения. В этом режиме команда tail будет работать и выводить строки по мере их добавления в файл до тех пор, пока вы не завершите ее работу, нажав Ctrl-c .

Команды expand, unexpand и tr

Когда мы создавали наши файлы text1 и text2, в последнем из них были использованы символы табуляции. Иногда вам может потребоваться заменить символы табуляции на символы пробела и наоборот. Для этого предназначены команды expand и unexpand . В обеих командах опция -t позволяет устанавливать позиции табуляции. Если после этой опции указывается только одно значение, то позиции табуляции будут периодически расставляться через этот указанный интервал. В листинге 10 показано, как сократить символы табуляции в файле text2 до одиночных пробелов, а также приведена причудливая последовательность из команд expand и unexpand и нарушающая выравнивание текста в файле text2.

Листинг 10. Использование команд expand и unexpand
$ expand -t 1 text2 9 plum 3 banana 10 apple $ expand -t8 text2|unexpand -a -t2|expand -t3 9 plum 3 banana 10 apple

К сожалению, вы не можете использовать команду unexpand для замены пробелов в файле text1 на символы табуляции, поскольку для преобразования в символ табуляции команде unexpand требуется, как минимум, два последовательных пробела. Однако вы можете использовать команду tr , которая преобразует символы из одного набора (набор1 ) в соответствующие символы из другого набора (набор2 ). В листинге 11 показан пример использования команды tr для преобразования пробелов в символы табуляции. Поскольку команда tr представляет собой фильтр в чистом виде, то входные данные для нее генерируются с помощью команды cat . В этом примере также показан пример использования дефиса (-) с целью указать cat на то, что ввод будет осуществляться со стандартного устройства; таким образом, мы можем объединить вывод команды tr с содержимым файла text2.

Листинг 11. Использование команды tr
$ cat text1 |tr " " "\t"|cat - text2 1 apple 2 pear 3 banana 9 plum 3 banana 10 apple

Если вы не очень понимаете, что происходит в последних двух примерах, то попробуйте использовать команду od , чтобы последовательно выполнить каждую команду конвейера, например:
cat text1 |tr " " "\t" | od -tc

Команды Pr, nl и fmt

Команда pr используется для форматирования файлов перед печатью. По умолчанию заголовок включает в себя имя файла, дату и время создания файла, номер страницы, а также две пустых строки нижнего колонтитула. Когда данные поступают из нескольких файлов или со стандартного устройства ввода, вместо даты и времени создания файла используются текущие дата и время. Можно печатать файлы рядом, каждый в своем столбце, а также управлять многими возможностями форматирования с помощью различных опций. Как обычно, дополнительную информацию вы можете найти на man-странице.

Команда nl нумерует строки, что может оказаться полезным при печати файлов. Для нумерации строк можно также использовать команду cat с опцией -n . В листинге 12 показано, как распечатать наш текстовый файл, пронумеровать строки в файле text2 и вывести его на печать вместе с файлом text1.

Листинг 12. Нумерация строк и форматирование перед печатью
$ pr text1 | head 2009-08-11 14:02 text1 Page 1 1 apple 2 pear 3 banana $ nl text2 | pr -m - text1 | head 2009-08-11 15:36 Page 1 1 9 plum 1 apple 2 3 banana 2 pear 3 10 apple 3 banana

Другой полезной командой для форматирования текста является команда fmt , которая форматирует текст таким образом, чтобы он не выходил за границы полей. Можно объединить несколько коротких строк в одну длинную и наоборот. В листинге 13 мы создали файл text3, используя одну длинную конструкцию из комбинаций символов!#:* (предназначенных для управления историей команд), благодаря чему, напечатанное предложение было сохранено в файле четыре раза. Также мы создали файл text4, содержащий по одному слову в каждой строке. Затем с помощью команды cat мы отобразили содержимое этих файлов в неформатированном виде, включая символ конца строки "$". Наконец, с помощью команды fmt мы отформатировали эти файлы, ограничив максимальное значение длины строки 60 символами. Как обычно, дополнительную информацию вы можете найти на man-странице.

Листинг 13. Форматирование с указанием максимальной длины строки
$ echo "This is a sentence. " !#:* !#:1->text3 echo "This is a sentence. " "This is a sentence. " "This is a sentence. ">text3 $ echo -e "This\nis\nanother\nsentence.">text4 $ cat -et text3 text4 This is a sentence. This is a sentence. This is a sentence. $ This$ is$ another$ sentence.$ $ fmt -w 60 text3 text4 This is a sentence. This is a sentence. This is a sentence. This is another sentence.

Команды sort и uniq

Команда sort сортирует входные данные с использованием схемы упорядочивания локали (LC_COLLATE) системы. Команда sort также может объединять уже отсортированные файлы и определять, является ли файл отсортированным или нет.

В листинге 14 приведены примеры использования команды sort для сортировки двух текстовых файлов после замены в файле text1 пробелов на символы табуляции. Поскольку сортировка выполняется на основе символьных значений, вы можете удивиться, увидев результаты. К счастью, команда sort может выполнять сортировку не только на основе символьных, но также и на основе числовых значений. Вы можете указать требуемый метод сортировки для всей записи или для каждого поля . Если вы не указываете разделитель полей, то используются пробелы или символы табуляции. Во втором примере листинга 14 сортировка первого поля выполняется по числовым значениям, а сортировка второго поля – с использованием схемы упорядочивания (в алфавитном порядке). Также показан пример использования опции -u для удаления повторяющихся строк.

Листинг 14. Сортировка по символьным и числовым значениям
$ cat text1 | tr " " "\t" | sort - text2 10 apple 1 apple 2 pear 3 banana 3 banana 9 plum $ cat text1|tr " " "\t"|sort -u -k1n -k2 - text2 1 apple 2 pear 3 banana 9 plum 10 apple

Заметьте, что в списке все равно присутствуют две строки со словом "apple", поскольку проверка уникальности выполнялась по всем ключам сортировки (в нашем случае это k1n и k2). Подумайте, какие команды нужно изменить или добавить в конвейер в последнем примере, чтобы исключить дублирование слова "apple".

Можно управлять удалением повторяющихся строк с помощью другой команды – uniq . В обычном режиме команда uniq работает с отсортированными файлами и удаляет последовательные повторяющиеся строки из любого файла независимо от того, отсортирован он или нет. Также эта команда может игнорировать заданные поля. В листинге 15 выполняется сортировка наших двух текстовых файлов по второму полю (имя фрукта), после чего удаляются строки, в которых повторяются значения второго поля (т. е. при проверке мы не обращаем внимания на первое поле).

Листинг 15. Использование команды uniq
$ cat text1|tr " " "\t"|sort -k2 - text2|uniq -f1 10 apple 3 banana 2 pear 9 plum

В этом примере сортировка выполнялась с использованием схемы упорядочивания, поэтому команда uniq оставила запись "10 apple", а не "1 apple". Вы можете добавить сортировку первого поля по числовым значениями и посмотреть, что изменится в этом случае.

Команды cut, paste и join

Давайте рассмотрим еще три команды, которые работают с полями в текстовых данных. Эти команды особенно полезны при работе с табличными данными. Первая команда cut извлекает поля из текстовых файлов. Символом-разделителем по умолчанию является символ табуляции. В листинге 16 содержится пример, в котором команда cut используется для разделения двух столбцов файла text2, а затем в качестве разделителя выходных данных используется пробел, что является необычным способом преобразования символов табуляции в пробелы.

Листинг 16. Использование команды cut
$ cut -f1-2 --output-delimiter=" " text2 9 plum 3 banana 10 apple

Команда paste вставляет (склеивает) строки из двух или более файлов, размещая их рядом (подобно тому, как команда pr объединяет файлы с помощью опции -m). В листинге 17 показан результат применения этой команды к нашим текстовым файлам.

Листинг 17. Склеивание файлов
$ paste text1 text2 1 apple 9 plum 2 pear 3 banana 3 banana 10 apple

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

Последняя команда для управления полями – это команда join , которая объединяет файлы на основе совпадения полей. Файл должен быть отсортирован по объединяемому полю. Поскольку файл text2 не отсортирован по числовым значениям, то можно отсортировать его, а затем объединить с помощью команды join две строки с одинаковым значением поля, по которому выполняется объединение (в нашем примере это первое поле, содержащее значение 3).

Листинг 18. Объединение файлов по совпадающим полям
$ sort -n text2|join -j 1 text1 - 3 banana banana join: file 2 is not in sorted order

Что же здесь пошло не так? Вспомните материал раздела , в котором говорилось о сортировке на основе числовых и символьных значений. Объединение выполняется по совпадающим символам в соответствии со схемой упорядочивания locale. Объединение не будет выполняться для числовых полей до тех пор, пока все поля не будут иметь одинаковую длину.

Мы использовали опцию -j 1 для объединения по первому полю в каждом файле. Для каждого файла можно указать отдельное поле, по которому будет выполняться объединение. Например, можно объединить поле 3 в одном файле с полем 10 другого файла.

Давайте создадим еще один файл, text5, выполнив сортировку файла text1 по второму полю (имя фрукта), а затем заменив пробелы на символы табуляции. Если теперь мы отсортируем файл text2 по второму полю и объединим его с файлом text5 по этому же полю, то получим два совпадения (apple и banana). Это объединение показано в листинге 19.

Листинг 19. Объединение файлов по совпадающим полям
$ sort -k2 text1|tr " " "\t">text5 $ sort -k2 text2 | join -1 2 -2 2 text5 - apple 1 10 banana 3 3

Редактор Sed

Sed (s tream ed itor) – это потоковый редактор. Ему посвящено несколько статей Web-сайта developerWorks, а также множество книг (см. раздел ). Sed является чрезвычайно мощным инструментом, а круг решаемых им задач ограничен лишь вашим воображением. Этот небольшой обзор должен пробудить ваш интерес к sed, хотя он не является полным и всесторонним.

Как и многие команды для работы с текстом, которые мы здесь рассмотрели, sed может работать как фильтр или принимать входные данные из файла. Вывод осуществляется на стандартное устройство вывода. Sed загружает строки из входных данных в область шаблонов , применяет к ее содержимому команды редактирования и передает ее на стандартное устройство вывода. Sed может объединять в области шаблонов несколько строк; результат может быть записан в файл, может быть записан частично, а может быть не записан вообще.

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

В листинге 20 показаны три простых сценария sed. В первом сценарии используется команда s (substitute – замена) для замены в каждой строке символа "a" в нижнем регистре на этот же символ в верхнем регистре. В первом примере выполняется замена только первого символа "a", поэтому во втором примере мы добавили флаг "g" (global – глобальный), благодаря которому, будет выполняться замена всех найденных вхождений этого символа. В третьем сценарии мы используем команду d (delete – удалить) для удаления строки. В нашем примере мы использовали адрес 2, чтобы показать, что необходимо удалить только строку с этим номером. Мы разделяем команды точкой с запятой (;) и используем глобальную замену символов "a" на "A", как это было сделано во втором примере.

Листинг 20. Первые шаги по работе со сценариями sed
$ sed "s/a/A/" text1 1 Apple 2 peAr 3 bAnana $ sed "s/a/A/g" text1 1 Apple 2 peAr 3 bAnAnA $ sed "2d;$s/a/A/g" text1 1 apple 3 bAnAnA

Помимо работы с отдельными строками, sed может работать с диапазонами строк. Начало и конец диапазона разделяются запятой (,) и могут определяться в виде номера строки, регулярного выражения или знака доллара ($), означающего конец файла. Зная адрес или диапазон адресов, вы можете сгруппировать несколько команд, заключив их в фигурные скобки { и }; таким образом, эти команды будут работать только с теми строками, которые указаны в диапазоне. В листинге 21 показано два примера глобальной замены, которая применяется только к последним двум строкам нашего файла. Также приведен пример использования опции -e для добавления нескольких команд в сценарий.

Листинг 21. Адреса в sed
$ sed -e "2,${" -e "s/a/A/g" -e "}" text1 1 apple 2 peAr 3 bAnAnA $ sed -e "/pear/,/bana/{" -e "s/a/A/g" -e "}" text1 1 apple 2 peAr 3 bAnAnA

Сценарии sed можно сохранять в виде файлов. Скорее всего, вы захотите использовать эту возможность для наиболее часто используемых сценариев. Вспомните команду tr , которую мы использовали для изменения пробелов в файле text1 на символы табуляции. Давайте теперь сделаем то же самое с помощью сценария sed, сохраненного в файле. Для создания файла мы используем команду echo . Результаты представлены в листинге 22.

Листинг 22. Короткая программа sed
$ echo -e "s/ /\t/g">sedtab $ cat sedtab s/ / /g $ sed -f sedtab text1 1 apple 2 pear 3 banana

Существует множество подобных коротких сценариев; ссылки на некоторые из них вы можете найти в разделе .

В нашем последнем примере сначала используется команда = для вывода номеров строк, а затем выполняется фильтрация полученного вывода с помощью sed (в результате мы получим такой же эффект, как от использования команды nl для нумерации строк). В листинге 23 с помощью команды = выводятся номера строк, затем с помощью команды N вторая строка ввода считывается в область шаблонов и, наконец, между двумя строками в области шаблонов удаляется символ новой строки (/n).

Листинг 23. Нумерация строк с помощью sed
$ sed "=" text2 1 9 plum 2 3 banana 3 10 apple $ sed "=" text2|sed "N;s/\n//" 19 plum 23 banana 310 apple

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

Листинг 24. Нумерация строк с помощью sed, второй подход
$ cat text1 text2 text1 text2>text6 $ ht=$(echo -en "\t") $ sed "=" text6|sed "N > s/^/ / > s/^.*\(......\)\n/\1$ht/" 1 1 apple 2 2 pear 3 3 banana 4 9 plum 5 3 banana 6 10 apple 7 1 apple 8 2 pear 9 3 banana 10 9 plum 11 3 banana 12 10 apple

Вот, что было сделано в этом примере:

  1. Сначала с помощью команды cat мы создали файл, содержащий 12 строк из двух копий файлов text1 и text2 (если бы количество строк оказалось меньше 10, т. е. все числа были бы одного порядка, то не было бы никакого смысла в их форматировании).
  2. В командном интерпретаторе bash клавиша табуляции используется для завершения команды, поэтому удобно иметь заранее определенный символ табуляции для использования в тех случаях, когда он вам потребуется. Для этого с помощью команды echo мы сохранили символ табуляции в переменной окружения "ht".
  3. Мы создали поток, содержащий номера строк и следующие за ними строки с данными, как мы делали это раньше, и отфильтровали его с помощью второй копии sed.
  4. Мы считали вторую строку в область шаблонов.
  5. В начале области шаблонов (обозначаемой символом ^) мы добавили к номеру строки префикс, состоящий из шести пробелов.
  6. Наконец, мы применили выравнивание с помощью разделителя, содержащего последние шесть символов и символ табуляции, и теперь в номера строк в выводе будут выровнены по первым шести символам. Обратите внимание на то, что в левой части команды "s" используются конструкции "\(" и "\)" для отметки символов, которые мы хотим использовать в правой части. В правой части мы ссылаемся на первый (и единственный в нашем примере) такой набор символов с помощью /1. Заметьте, что наша команда заключена в двойные кавычки ("), поэтому эта подстановка будет выполнена для переменной $ht.

Последняя (четвертая) версия редактора sed содержит документацию в формате info и включает множество превосходных примеров. В более старой версии, 3.02, эти возможности отсутствуют. Узнать версию редактора GNU sed можно с помощью команды sed --version .

Уже некоторое время поработав в Linux, понабирав команды в командной строке, Мефодий пришёл к выводу, что в общении с оболочкой не помешают кое-какие удобства. Одно из таких удобств - возможность редактировать вводимую строку с помощью клавиши Backspace (удаление последнего символа), « ^W » (удаление слова) и « ^U » (удаление всей строки) - предоставляет сам терминал Linux. Эти команды работают для любого построчного ввода: например, если запустить программу cat без параметров, чтобы та немедленно отображала вводимые с терминала строки. Если по каким-то причинам в строчку на экране влез мусор, можно нажать « ^R » (r edraw) - система выведет в новой строке содержимое входного буфера.

Мефодий не забыл, что cat без параметров следует завершать командой « ^D » (конец ввода). Эту команду, как и предыдущие, интерпретирует при вводе с терминала система. Система же превращает некоторые другие управляющие символы (например, « ^C » или « ^Z ») в сигналы . В действительности все управляющие символы, интерпретируемые системой, можно перенастроить с помощью команды stty . Полный список того, что можно настраивать, выдаёт команда stty -a:

$ stty -a localhost 38400 baud; rows 30; columns 80; line = 0; intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0; -parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts -ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel -iutf8 opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke

Пример 1 . Настройки терминальной линии

При виде столь обширных возможностей Мефодий немедленно взялся читать руководство (man stty), однако нашёл в нём не так уж много для себя полезного. Из управляющих символов (строки со второй по четвёртую) интересны « ^S » и « ^Q », с помощью которых можно, соответственно, приостановить и возобновить выдачу на терминал (если текста вывелось уже много, а прочесть его не успеваешь). Можно заметить, что настройка erase (удаление одного символа) соответствует управляющему символу, который возвращается клавишей Backspace именно виртуальной консоли Linux - « ^? ». На многих терминалах клавиша Backspace возвращает другой символ - « ^H ». Если необходимо переопределить настройку erase , можно воспользоваться командой « stty erase ^H », причём « ^H » (для удобства) разрешено вводить и как два символа: « ^ » и « H ».

Наконец, чтобы лишить передаваемый символ его управляющих функций (если, например, требуется передать программе на ввод символ с кодом 3 , т. е. « ^C »), непосредственно перед вводом этого символа нужно подать команду « ^V » (lnext):

$ cat | hexdump -C Сейчас нажмём Ctrl+C $ cat | hexdump -C Теперь Ctrl+V, Ctrl+C, enter и Ctrl+D^C 00000000 f4 c5 d0 c5 d2 d8 20 43 74 72 6c 2b 56 2c 20 43 |Теперь Ctrl+V, C| 00000010 74 72 6c 2b 43 2c 20 45 6e 74 65 72 20 c9 20 43 |trl+C, enter и C| 00000020 74 72 6c 2b 44 03 0a |trl+D..| 00000027

Пример 2 . Экранирование управляющих символов

Здесь Мефодий прервал, как и собирался, работу первого из cat . При этом до hexdump , фильтра, переводящего входной поток в шестнадцатеричное предстваление, дело даже не дошло, потому что cat не успел обработать ни одной строки. Во втором случае « ^C » после « ^V » потеряло управляющий смысл и отобразилось при вводе. С ключом « -C » hexdump выводит также и текстовое предстваление входного потока, заменяя непечатные символы точками. Так на точки были заменены и « ^C » (ASCII-код 03), и возвращаемый Enter символ конца строки (ASCII-код 0a , в десятичном виде - 12). Ни « ^V », ни « ^D » на вход hexdump , конечно, не попали: их, как управляющие, обработала система.

Прочие настройки stty относятся к обработке текста при выводе на терминал и вводе с него. Они интересны только в том смысле, что при их изменении работать с комндной оболочкой становится неудобно. Например, настройка echo определяет, будет ли система отображать на экране всё, что вводит пользователь. При включённом echo нажатие любой алфавитно-цифровой клавиши (ввод символа) приводит к тому, что система (устройство типа tty) выведет этот символ на терминал. Настройка отключается, когда с клавиатуры вводится пароль. При этом трудно отделаться от ощущения, что ввода с клавиатуры не происходит. Ещё хуже обстоит дело с настройками, состоящими из кусков вида « i », « o », « cr » и « nl ». Эти настройки управляют преобразованием при вводе и выводе исторически сложившегося обозначения конца строки двумя символами в один , принятый в Linux. Может случиться так, что клавиша Enter терминала возвращает как раз неправильный символ конца строки, а преобразование отключено. Тогда вместо Enter следует использовать « ^J » - символ, на самом деле соответствующий концу строки.

Во всех случаях, когда терминал находится в непонятном состоянии - не реагирует на Enter , не показывает ввода, не удаляет символов, выводит текст «ступеньками» и т. п., рекомендуется «лечить» настройки терминала с помощью stty sane - специальной формы stty , сбрасывающей настройки терминала в некоторе пригодное к работе состояние. Если непонятное состояние терминала возникло однократно, например, после аварийного завершения экранной программы (редактора vim или оболочки mc), то можно воспользоваться командой reset . Она заново настраивает терминал в полном соответствии с системной конфигурацией (указанной в файле /etc/inittab , см. лекцию Этапы загрузки системы) и terminfo .

Если терминал ведёт себя странно, последовательность « ^J stty sane^J » может его вылечить!

Редактирование командной строки

Даже не изучая специально возможностей командной оболочки, Мефодий активно использовал некоторые из них, не доступные при вводе текста большинству утилит (в частности, ни cat , ни hexdump). Речь идёт о клавишах Стрелка влево и Стрелка вправо , с помощью которых можно перемещать курсор по командной строке, и клавише Del , удаляющей символ под курсором, а не позади него. В лекции Терминал и командная строка он уже убедился, что эти команды работают в bash , но не работают для cat . Более того, для простого командного интерпретатора - sh - они тоже не работают.

Следовательно, возможности редактора командной строки специфичны для разных командных оболочек. Однако самые необходимые команды редактирования поддерживаются во всех разновидностях shell сходным образом. По словам Гуревича «во всех видах Linux обязательно есть bash , а если ты достаточно опытен, чтобы устанавливать и настраивать пакеты, можешь установить zsh , у него возможностей больше, чем может понадобиться одному человеку». Поэтому Мефодий занялся изучением документации по bash , что оказалось делом непростым, ибо в bash.info он насчитал более восьми с половиной тысяч строк. Даже про редактирование командной строки написано столько, что за один раз прочесть трудно.

Попытка «наскоком» узнать всё про работу в командной строке принесла некоторую пользу. Во-первых, перемещаться в командной строке можно не только по одному символу вперёд и назад, но и по словам: команды ESCF/ESCB или Alt+F/Alt+B соответственно (от f orward и b ckward), работают также клавиши &home& и &end& , или, что то же самое, « ^A » и « ^E ». А во-вторых, помимо работы с одной командной строкой, существует ещё немало других удобств, о которых и пойдёт речь в этой лекции.

История команд

Двумя другими клавишами со стрелками - вверх и вниз - Мефодий тоже активно пользовался, не подозрвая, что задействует этим весьма мощный механизм bash - работу с историей команд . Все команды, набранные пользователем, bash запоминает и позволяет обращаться к ним впоследствии. По стрелке вверх (можно использовать и « ^P », p revious), список поданных команд «прокручивается» от последней к первой, а по стрелке вниз (« ^N », n ext) - обратно. Соответствующая команда отображается в командной строке как только что набранная, её можно отредактировать и подать оболочке (подгонять курсор к концу строки при этом не обязательно).

Если необходимо добыть из истории какую-то давнюю команду, проще не гонять список истории стрелками, а поискать в ней с помощью команды « ^R » (r everse search). При этом выводится подсказка специального вида («(reverse-i-search)»), подстрока поиска (окружённая символами ` и ") и последняя из команд в истории, в которой эта подстрока присутствует:

$ ^R | (reverse-i-search)`": i | (reverse-i-search)`i": ls i n | (reverse-i-search)`in": info f | (reverse-i-search)`inf": info o | (reverse-i-search)`info": info ^R | (reverse-i-search)`info": man info ^R | (reverse-i-search)`info": info "(bash.info.bz2)Commands For History"

Пример 3 . Поиск по истории команд

Пример представляет символы вводимые Мефодием (в левой части до « | ») и содержимое последней строки терминала. Это «кадры» работы с одной и той же строкой, показывающие, как она меняется при наборе. Набрав «info», Мефодий продолжил поиск этой подстроки, повторяя « ^R » до тех пор, пока не наткнулся на нужную ему команду, содержащую подстроку « info ». Осталось только передать её bash с помощью Enter .

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

Сокращения

Поиск по истории - удобное средство: длинную командную строку можно не набирать целиком, а выискать и использовать. Однако давнюю команду придётся добывать с помощью нескольких « ^R » - а можно и совсем не доискаться, если она уже выбыла оттуда. Для того, чтобы оперативно заменять короткие команды длинными, стоит воспользоваться сокращениями (aliases). В конфигурационных файлах командного интерпретатора пользователя обычно уже определено несколько сокращений, список которых можно посмотреть с помощью команды alias без параметров:

$ alias alias cd..="cd .." alias cp="cp -i" alias l="ls -lapt" alias ll="ls -laptc" alias ls="ls --color=auto" alias md="mkdir" alias mv="mv -i" alias rd="rmdir" alias rm="rm -i"

Пример 4 . Просмотр заранее определённых сокращений

С сокращениями Мефодий уже сталкивался в лекции Права доступа , где команда ls отказалась работать в согласии с теорией. Выяснилось, что по команде ls вместо утилиты /bin/ls bash запускает собственную команду-сокращение, превращающееся в команду ls --color=auto . Повторно появившуюся в команде подстроку « ls » интерпретатор уже не обрабатывает, во избежание вечного цикла. Например, команда ls -al превращается в результате в ls --color=auto -al . Точно так же любая команда, начинающаяся с rm , превращается в rm -i (i nteractive), что Мефодия крайне раздражает, потому что ни одно удаление не обходится без вопросов в стиле «rm: удалить обычный файл ` файл "?».

$ unalias cp rm mv $ alias pd=pushd $ alias pp=popd $ pd /bin /bin ~ $ pd /usr/share/doc /usr/share/doc /bin ~ $ cd /var/tmp $ dirs /var/tmp /bin ~ $ pp /bin ~ $ pp ~ $ pp -bash: popd: directory stack empty

Пример 5 . Использование сокращений и pushd/popd

От надоедливого « -i » Мефодий избавился с помощью команды unalias , а заодно ввёл сокращения для полюбившихся ему команд bash - pushd и popd . Эти команды, подобно cd , меняют текущий каталог.

Они названы по аналогии с операциями работы со стеком - push и pop .

Разница состоит в том, что pushd все каталоги, которые пользователь делает текущими, запоминает в особом списке (стеке). Команда popd удаляет последний элемент этого стека, и делает текущим каталогом предпоследний. Обе команды вдобавок выводят содержимое стека каталогов (то же самое делает и команда dirs). Команда cd в bash также работает со стеком каталогов: она заменяет его последний элемент новым.

Команда-сокращение Внутренняя команда shell, задаваемая пользователем. Обычно заменяет одну более длинную команду, которая часто используется при работе в командной строке. Сокращения не наследуются с окружением.

Достраивание

Сокращения позволяют быстро набирать команды , однако никак не затрагивают имён файлов , которые чаще всего и оказываются параметрами этих команд. Бывает, что набранной строки - пути к файлу и нескольких первых букв его имени - достаточно для однозначного указания на этот файл, потому что по введённому пути болшьше файлов, чьё имя начинается на эти буквы, просто нет. Чтобы не дописывать оставшиеся буквы (а имена файлов в Linux могут быть весьма длинными), Гуревич посоветовал Мефодию нажать клавишу Tab . И - о чудо! - bash сам достроил начало имени файла до целого (снова воспользуемся методом «кадров»):

$ ls -al /bin/base Tab | $ ls -al /bin/basename -rwxr-xr-x 1 root root 12520 Июн 3 18:29 /bin/basename $ base Tab | $ basename Tab | $ basename ex Tab | $ basename examples/ Tab | $ basename examples/-filename-with- -filename-with-

Пример 6 . Использование достраивания

Дальше - больше. Оказывается, и имя команды можно вводить не целиком: оболочка догадается достроить набираемое слово именно до команды, раз уж это слово стоит в начале командной строки. Таким образом, команду basename examples/-filename-with- Мефодий набрал за восемь нажатий на клавиатуру (« base » и четыре Tab)! Ему не пришлось вводить начало имени файла в каталоге examples , потому что файл там был всего один.

Выполняя достраивание (completion), bash может вывести не всю строку, а только ту её часть, относительно которой у него нет сомнений. Если дальнейшее достраиване может пойти несколькими путями, то однократное нажатие Tab приведёт к тому, что bash растерянно пискнет, а повторное - к выводу под командной строкой списка всех возможных вариантов.

Все терминалы должны уметь выдавать звуковой сигнал при выводе управляющего символа « ^G ». Для этого не нужно запускать никаких дополнительных программ: «настоящие» терминалы имеют встроенный динамик, а виртуальные консоли обычно пользуются системным («пищалкой»). В крайнем случае разрешается привлекать внимание пользователя другими способами: например, эмулятор терминала screen пишет в служебной строке «wuff-wuff» («гав-гав»).

В этом случае надо подсказать командной оболочке продолжение: дописать несколько символов, определяющих, по какому пути пойдёт достраивание, и снова нажать Tab .

Поиск ключевого слова «completion» по документации bash выдал так много информации, что Мефодий обратился к Гуревичу за помощью. Однако тот ответил, что не использует bash , и поэтому не в состоянии объяснять тонкости его нстройки. Если в bash - несколько типов достраивания (по именам файлов, по именам команд и т. п.), то в zsh их сколько угодно : существует способ запрограммировать любой алгоритм достраивания и задать шаблон командной строки, в которой именно этот способ будет применяться.