11.8. Управление временным файлом

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

Приведу пример того, как использовать команду mktemp для создания имен временных файлов. Данный сценарий показывает аппаратные прерывания, которые возникли за последние две секунды.

#!/bin/sh

TMPFILE1=$(mktemp /tmp/im1.XXXXXX)

TMPFILE2=$(mktemp /tmp/im2.XXXXXX)

cat /proc/interrupts > $TMPFILE1

sleep 2

cat /proc/interrupts > $TMPFILE2

diff $TMPFILE1 $TMPFILE2

rm -f $TMPFILE1 $TMPFILE2

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

примечание

Не все варианты Unux содержат команду mktemp. Если у вас возникнут проблемы с переносимостью, лучше установить GNU-пакет coreutils для вашей операционной системы.

Еще одна проблема сценариев, которые используют временные файлы, такова: если выполнение сценария будет прервано, временные файлы могут остаться в си­стеме. В предыдущем примере, если нажать сочетание клавиш Ctrl+C до начала второй команды, то в каталоге /tmp останется временный файл. Избегайте этого по возможности. Старайтесь использовать команду trap для создания обработчика сигнала, который будет перехватывать сигнал от нажатия клавиш Ctrl+C и удалять временные файлы, как в этом примере:

#!/bin/sh

TMPFILE1=$(mktemp /tmp/im1.XXXXXX)

TMPFILE2=$(mktemp /tmp/im2.XXXXXX)

trap "rm -f $TMPFILE1 $TMPFILE2; exit 1" INT

snip

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

примечание

Не обязательно передавать аргументы команде mktemp. Если их нет, то шаблон будет начинаться с префикса /tmp/tmp.

11.9. Синтаксис heredoc

Допустим, вам необходимо вывести большой фрагмент текста или передать его другой команде. Чтобы не использовать несколько команд echo, можно применить синтаксис heredoc, как показано в следующем сценарии:

#!/bin/sh

DATE=$(date)

cat <<EOF

Date: $DATE

The output above is from the Unix date command.

It's not a very interesting command.

EOF

Элементы, выделенные жирным шрифтом, управляют синтаксисом heredoc. Маркер <<EOF дает оболочке указание перенаправить все строки, которые последуют дальше, в стандартный ввод команды, предшествующей маркеру <<EOF (в данном случае это команда cat). Перенаправление прекращается, как только в какой-либо строке появляется единственный маркер EOF. На самом деле этот маркер может быть любым, просто помните о том, что в начале и в конце фрагмента с синтаксисом heredoc следует указать одинаковые маркеры. По принятому соглашению название маркера должно быть набрано прописными буквами.

Обратите внимание на переменную $DATE в приведенном примере. Оболочка раскрывает переменные оболочки внутри документов с синтаксисом heredoc, это особенно полезно, когда вы выводите отчеты, которые содержат много переменных.

11.10. Основные утилиты в сценариях оболочки

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

11.10.1. Команда basename

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

$ basename example.html .html

$ basename /usr/local/bin/example

В обоих случаях команда basename возвращает результат example. Первая команда удаляет суффикс .html из имени файла example.html, а вторая — названия каталогов из полного пути.

Следующий пример демонстрирует, как применить команду basename в сценарии, который конвертирует файлы изображений из формата GIF в формат PNG:

#!/bin/sh

for file in *.gif; do

    # exit if there are no files

    if [ ! -f $file ]; then

        exit

    fi

    b=$(basename $file .gif)

    echo Converting $b.gif to $b.png...

    giftopnm $b.gif | pnmtopng > $b.png

done

11.10.2. Команда awk

Команда awk не является простой командой с единственным способом применения; на самом деле это мощный язык программирования. К сожалению, искусство применения языка awk сейчас практически утрачено, поскольку его заменили более развитые языки, такие как Python.

Языку awk посвящены целые книги, например The AWK Programming Language («Язык программирования AWK») Альфреда В. Эйхо (Alfred V. Aho), Брайана Кернигана (Brian W. Kernighan) и Питера Дж. Вайнбергера (Peter J. Weinberger) (Addison-Wesley, 1988). Очень многие пользователи используют команду awk с единственной целью: чтобы выбрать отдельное поле из потока ввода, как здесь:

$ ls -l | awk '{print $5}'

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

11.10.3. Команда sed

Команда sed (сокращение от stream editor — «редактор потока») является автоматическим текстовым редактором, который принимает входящий поток (файл или стандартный ввод), изменяет его в соответствии с некоторым выражением и выводит результат в стандартный вывод. Во многих отношениях команда sed подобна команде ed, первичному текстовому редактору Unix. Она обладает множеством операций, инструментами подстановки и возможностями работы с адресацией. Как и для команды awk, есть книги и о команде sed, среди которых краткий справочник по обеим командам: sed & awk Pocket Reference («Карманный справочник по командам sed и awk») Арнольда Роббинса (Arnold Robbins), 2-е издание (O’Reilly, 2002).

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

Очень распространенная задача для команды sed: заменить какое-либо регулярное выражение текстом (см. подраздел 2.5.1), например, так:

$ sed 's/exp/text/'

Так, если вы желаете заменить первое двоеточие в файле /etc/passwd на символ %, а затем отправить результат в стандартный вывод, следует выполнить следу­ющую команду:

$ sed 's/:/%/' /etc/passwd

Чтобы заменить все двоеточия в файле /etc/passwd, добавьте спецификатор g в конце операции, как здесь:

$ sed 's/:/%/g' /etc/passwd

Приведу команду, которая работает построчно; она считывает файл /etc/passwd и удаляет строки с третьей по шестую, а затем отправляет результат в стандартный вывод:

$ sed 3,6d /etc/passwd

В этом примере число 3,6 является адресом (диапазоном строк), а флаг d — операцией (удаление). Если адрес опустить, команда sed будет работать со всеми строками входного потока. Двумя самыми распространенными операциями команды sed являются s (найти и заменить) и d.