Содержание
Оболочка shell
На этой странице будут собраны и разбиты на разделы практические приёмы использования оболочки (shell). Так как сама по себе оболочка является всего лишь тонким «клеем» для объединения программ и файлов в новые инструменты (через конвеер и перенаправление ввода-вывода), в этот раздел будут помещены ссылки на другие вики-страницы, где описаны приёмы работы с прочими классическими утилитами Unix.
Конвеер для работы с файлами
$ find /etc -maxdepth 1 -type f | sort | ( echo cat ; cat ) | paste -s -d ' ' | sh >/tmp/all
Я бы отдал предпочтение tr вместо paste, вот так:
$ find /etc -maxdepth 1 -type f | sort | { echo cat; cat; } | tr '\n' ' ' | sh >/tmp/all
Здесь запуск подоболочки (subshell) заменён на блок (в фигурных скобках), так как преследовалась цель объединить последовательно вывод утилит echo и cat на стандартный вывод. Для этой цели подоболочку запускать расточительно.
Здесь есть ещё одна потенциальная проблема, она связана с тем, что в именаx файлов могут встречаться любые символы, кроме нулевого байта (NUL) и слеша (/). В имени файла может присутствовать символ новой строки (NL), в то же время find печатает список файлов, отделяя этим символом имена друг от друга. Если в имени файла содержится символ новой строки, тогда вывод find не отличим от двух файлов, следующих подряд.
Если используются утилиты GNU, то следует воспользоваться расширением, которое заменяет символ разделителя в списке файлов, выводимом find:
$ find /etc -maxdepth 1 -type f -print0 | sort -z | xargs -0 cat >/tmp/all
Здесь find выводит имена файлов разделяя их не символом новой строки, а нулевым байтом (NUL). В этом случае все утилиты конвеера должны понимать это соглашение, например, GNU sort и GNU xargs умеют обрабатывать списки файлов с таким разделителем. -- СиткаревГригорий
Фильтр sed
$ echo "abcabc" | sed 's/\(.*\)\(\1\)/\1#\2/' abc#abc
В данном примере в регулярном выражении используется backreference. Регулярное выражение состоит из двух групп выделенных круглыми скобками. Первой группе соотвествует любой набор символов .*, во второй группе используется backreference \1 (отсыл на первое выражение), при чем эта группа выделит выражение идентичное первому. В выводе мы адресуем группы как \1 и \2 и разделяем их символом #.
sed как язык программирования
На сегодняшний день sed в основном испозуется для замены или выделения выражений из входных данных. Но этот инструмент намного мощнее. sed это небольшой язык программирования со своей средой исполнения.
Далее текст можно рассматривать как небольшое введение по программированию на sed.
sed имеет два буфера, первый -- pattern буфер -- это входной буфер, он заполняется автоматически при обработке входных данных (или принудительно смотри команды n, N), второй -- hold буфер -- буфер хранения, данные в этот буфер могут попасть только по вызову специальной команды (смотри команды h, H). Пользователь может копировать, добавлять данные из одного буфера в другой, обменивать их содержимым друг с другом.
Команды могут объединяться в группы фигурными скобками:
{ action1; action2... }а так же выполняться с условием, подобно ЯзыкAWK:
condition { action }
где условие (condition) может быть регулярным выражением, номером строки или диапазоном строк.
Напечатать на экран строки с 5-ой по 8-ую:
$ sed -n '5,8p' < /etc/passwd
p -- печатает содержимое pattern буфера.
Напечатать на экран лог ошибки сервера Apache за определенный период:
$ sed -n '/^\[.*Feb 08/,/^\[.*Feb 10/p' < /var/log/apache2/error.log
/regexp1/,/regexp2/ -- условие задается двумя регулярными выражениями -- начальной и конечной датой.
Удалить все закоментированные и пустые строки:
$ sed -e '/^ *#/d' -e '/^$/d' </etc/ssh/sshd_config
d -- удаляет pattern буфер, если условие верно, /^ *#/ строка с коментариями, /^$/ пустая строка, переходит к следующему циклу чтения, все последующие команды игнорируются.
Следующий пример будет сложнее:
$ sed -n 'H;${x;s/\n/ /g;p}' < /etc/passwd
H -- добавляет все входные строки из pattern буфер к данным в hold буфер, таким образом, мы накапливаем все входные строки в буфере хранения;
${ ... } -- $ означает последнюю строку, т.е все действия в фигурных скобках выполняться только при обработке последней строки;
x -- обменивает содержимое hold буфера с содержимым pattern буфера, т.е все накопленные данные перемещаются из hold в ptatern буфер, а из pattern в hold буфер;
s/\n/ /g -- команда замены в pattern буфере, где теперь находятся все наши строки, меняем символ переноса -- пробелом (g делаем это глобально, для всех \n);
p -- печатает pattern буфер. Все строки теперь объеденены в одну.
Слегка расширим предыдущий пример.
$ sed -n '1h;1!H;${x;s/\n/ /g;p}' < /etc/passwd
1h -- первую строку копируем в hold буфер;
1!H -- все входные строки кроме первой добавляем в hold буфер, т.е дописываем к уже имеющимся в нем данным, ! знак инвертирования условия, в нашем случаи оно выполняется для всех строк кроме первой.
В остальном скрипт повторяет предыдущий.
Стоит отметить, что накопление значительных объемов данных может привести к переполнению внутренних буферов sed. Этого не должно произойти при использовании GNU sed.
Прокомментируйте, пожалуйста, что делают вышеперечисленные примеры вызова sed. -- СиткаревГригорий
КатегорияОболочки | КатегорияЯзыкиПрограммирования