Одним из следствий У (точнее, правила
Формально текстовый файл - это файл, разбитый на \n ", ascii-код 12), в обработанном режиме именно
Вообще, каждый тип текстового файла имеет свои представления о лексике (правилах построения
В отличие от текста на естественном языке (например, на русском), тексты, составляющие различные проекты в UNIX, строго структурированы. Языки этих текстов имеют четкий синтаксис и довольно простую лексическую организацию. Если перед человеком стоит задача исправления проекта, для решения которой достаточно видоизменить имеющийся текст, а дописывать новые части проекта не надо, ее можно решать двумя способами: либо выбрасывать устаревшие куски и вписывать новые (что равносильно дописыванию), либо поручить эту работу роботу. Робот должен - на уровне, достаточном для данной работы, - разбираться в синтаксисе и лексике языка и иметь четкие инструкции, какие куски считать устаревшими и как их преобразовывать в новые.
Иными словами, стандартная задача обработки текста состоит из поиска фрагмента текста заданной структуры и преобразования этого фрагмента согласно заданному алгоритму. Таким способом изменять проект зачастую гораздо быстрее и уж точно не в пример интереснее, чем механически выполнять работу, которую может сделать робот. Типичный пример выполнимой задачи - поиск определенного
С задачей поиска помогает справиться мощный механизм представления структуры текста, называемый
Лексический анализ большинства языков в UNIX и большая часть синтаксического анализа делается именно на основе
Про
'регулярное выражение', а другие - так: строка.
Любой обычный символ - это f соответствует 'f'.
Спецсимвол . - M или @ соответствуют '.', аb или abc - не соответствуют (хотя abc содержит три фрагмента - a, b и c, соответствующих '.' ).
Конструкция из [ и ], между которыми находится один или несколько других символов, - это x и u соответствуют '[unix]', а U и 8 - не соответствуют. - ", который задает - ", и не больше, чем у стоящего справа. Например, любая буква латинского алфавита соответствует выражению '[a-zA-Z]'. Если нужно включить в - ", он должен идти сразу за [ или перед ]. Если при определении [ использовать [^, такому A и ; соответствуют выражению '[^aeiu-z]', а e и v - не соответствуют.
Последовательность '[1-9][0-9]': в числе, допустим, 36 3 соответствует '[1-9]', а 6 - '[0-9]'. Последовательность
*, - тоже 'a*' соответствуют подстроки a, aa, aaa и т. д. Выражению '[0-9]*' соответствует любая (включая пустую) последовательность десятичных цифр, например 1415926, потому что ее можно разбить на односимвольные фрагменты, каждый из котороых будет соответствовать выражению '[0-9]'. Наконец, выражению '(Foo)*' будут соответствовать подстроки Foo, FooFoo, FooFooFoo и т. д., но не будут соответствовать подстроки Fo, FFFoooooo, FFoo и Fwoo, так как их невозможно целиком разбить на фрагменты, соответствующие '(Foo)'. Любой подстроке любой длины соответствует выражение '.*'.
{минимум,максимум}, также '(he){2,4}' соответствуют подстроки hehe, hehehe и hehehehe. Если в 'Z{2,}' соответствуют подстроки ZZ, ZZZ, ZZZZ и т. д., а выражению 'w{,3}' - подстроки w, ww и www.
Спецсимволы !. Таким образом, выражению '^[a-z]{1,}$' будут соответствовать любые
Спецсимвол \ лишает следующий за ним любой спецсимвол специального значения (известная уже нам операция закавычивания). Например, выражению '\.\.\.' соответствует многоточие, а выражению '\$\*' - подстрока $*.
На этом описание
Операция выбора - .
Два частных случая повторителя-интервала: '{,1}' ), и +, обозначающий использование предшествующего '{1,}' ). Оба частных случая постоянно встречаются в повседневной работе; кроме того, обработка + во многих случаях идет существенно быстрее, чем обработка *, поэтому + стоит использовать вместо * везде, где только возможно.
В '[[:alnum:]]' (ему соответствует одна буква или одна цифра), или '[[:blank:]]' (ему соответствует один '[[:alpha:]]' или '[[:alnum:]]'. По этой же причине рекомендуется не использовать
| |
Соответствует | Не соответствует |
|---|---|---|
'a.c' |
abc, aWc, a+c |
ac, a..c, |
'F.*d' |
FddFd, Freud, F.*d |
feed, Sigmund, dF |
'u..[p-t]' |
user, u##s, uppp |
undo, uncut, u=t |
'wuff(-wuff)*' |
wuff, wuff-wuff-wuff |
wuffwuff, wuff- |
'[+-]?[0-9]+' |
+1, -0932, 333 |
+-7, -, 0+0 |
'[fit|plug](in|out)+' |
fitin, pluginoutin |
infit, plug, outin |
К $ и ^ добавлено еще два '[[:<:]]' и '[[:>:]]', которым соответствует начало и конец '[[:>:]]' зависит от символа, который не входит в подстроку-результат, т. е. зависит от контекста.
Новый синтаксис РВ получил название
Подстрока, соответствующая РВ, выбирается, как уже говорилось, из
Избавиться от неоднозначности при поиске подстроки в 'a.*d' в The syntax for word boundaries is incredibly ugly будет соответствовать подстрока от самого левого a до самого правого d: ax for word boundaries is incred.
Выражению 'B*' в BBcBBBd можно найти 17 соответствий (из них одна подстрока BBB, 3 BB, 5 B и 8 пустых подстрок в различных позициях). Правильным ответом будет BB в начале B и BB ). А вот в aBBcBBBd первой будет найдена пустая подстрока, потому что 'B*' имеет cоответствие в самом начале
Если некоторая подстрока может быть поставлена в соответствие '[0-9]+[0-9]+' в a123c5678d будет найдена подстрока 123, причем 12 будет соответствовать первому '[0-9]+', а 3 - второму.
Почему так? Во-первых, 5678 отпадает, потому что есть соответствие, которое лежит ближе к началу 123. Во-вторых, будет найдено именно 123, а не 1 или 12, потому что 123 - самое длинное из возможных соответствий. В-третьих, начальное '[0-9]+' и радо бы захватить для себя всю 123, но тогда на долю второго не остается ничего, и соответствия всему РВ не получается. Так что оно вынуждено уступить второму '[0-9]+' хотя бы один символ - 3.
Подобное поведение
Поиском подстрок в *grep - fgrep, grep и egrep. Первая из них - fgrep ( f ast grep) - не использует grep интерпретирует свой первый параметр как egrep работает с grep. Об имени ( фамилии?) самого семейства - grep - рассказано
в лекции 15.
Все утилиты семейства *grep распознают ключ -i ( i gnore case), при котором строчные и прописные буквы не различаются, ключ -v (in v ert), при котором выдаются -l, при котором выдаются только номера подходящих -q ( q uiet, в cтарых версиях grep - -s, s ilent), при котором вообще ничего не выводится, а команда используется ради кода ошибки, равного нулю, только когда шаблон найден. Кроме того, если в командной строке *grep всего один параметр, который не является ключом, этот параметр распознается как шаблон, а текст считывается со стандартного ввода; если же таких параметров несколько, то второй и последующие распознаются как имена файлов, и текст считывается из них. Наконец, если параметр-шаблон состоит из нескольких
Еще раз повторим, что задача grep - выбрать grep неважно, важно только, нашлось или нет. В таком виде эта утилита весьма полезна при работе с системой и применяется повсеместно; нам настолько тяжело было отказаться от нее в наших примерах, что пришлось без особых объяснений ввести ее еще в лекции 6 для ограничения контекста в команде .
Пример. Допустим, мы пишем сценарий на shell, в котором нам понадобится полное имя пользователя, запустившего этот сценарий. Полное имя пользователя (оно же GECOS) хранится в файле /etc/passwd. Для того чтобы извлечь его оттуда, надо знать входное имя пользователя, оно выдается командой logname. На всякий случай набираем в терминале
$ logname max $ egrep $(logname) /etc/passwd max:x:510:500:Max B. Tough:/home/max:/bin/sh maxim:x:541:500:Maxim Outsider:/home/maxim:/bin/sh sorrow:x:612:600:Temporary account:/home/shamaxe:/bin/sh
А вот и ошибка! Нас интересует входное имя, а не любая подстрока из :":
$ egrep "^$(logname):" /etc/passwd max:x:510:500:Max B. Tough:/home/max:/bin/sh
Теперь осталось выделить из этой cut, которая как раз предназначена для вывода некоторых полей. Напишем команду в виде, готовом для сценария (выполним еще одну подстановку и присвоим результат переменной GECOS):
$ GECOS=$(egrep "^$(logname):" /etc/passwd | cut -d: -f5) $ echo $GECOS Max B. Tough
Готово!
less либо more используется командой man, когда та показывает руководство, так что поиск в это время может очень пригодиться.
У less, кстати, есть одно незаметное для пользователя, но крайне полезное свойство. Показывая страницу помощи, less, как может, подкрашивает ее (подчеркиванием и ярким шрифтом). Так вот, операция поиска не зависит от того, какая часть найденной A+^H+A (напомним, что ^H - условное обозначение символа backspace (ascii-код 8, в восьмеричном виде - 10)), а подчеркнутая буква F - как _+^H+F. Однако, к счастью, команда /bn действительно находит bn в руководстве по less, хотя n выделена подчеркиванием. А grep без посторонней помощи не может справиться с такой задачей, приходится вызывать colcrt(1):
$ man less | colcrt | egrep bn
-bn or --buffers=n
Настало время подлить ложку дегтя в бочку меда, которой представляется '**' или '+?', и многие \ -последовательности ( '\n' - это n или перевод
В теоретическом подходе символы, управляющие разбором [ имеют специальное значение везде в РВ, а " ^ " и " $ " - соответственно в начале и в конце. Все остальные символы считаются обычными, а если они должны быть специальными, тогда перед ними надо поставить " \ ". В \( и \), а в \{ и \}. В таком виде grep, sed, more, vi и некоторые другие. Если программа использует библиотеку GNU \ ко всем спецсимволам, которые в grep, и sed, и more понимают шаблон 'etc\|bin' и исправно находят etc или bin:
linux$ ls -1 / | grep "etc\|bin" bin etc sbin
Наконец, когда программисты расширяют синтаксис \b (и начало \< и \> соответственно. Именно в Perl и pcre (perl compatible pcre(3) ). Похожие, тоже весьма мощные, диалекты PB используются в Python и некоторых других языках программирования. Собственный диалект
Таким образом, существует несколько
До этого мы все время говорили только об операции поиска подстроки в
Для поиска с заменой можно воспользоватья утилитой sed ( s tream ed itor - поточный редактор). Редактор sed весьма точно соответствует идеологии автоматического исправления проекта: это текстовый редактор, которым управляет не пользователь, а заданный sed -сценарий. Язык sed довольно своеобразен и слегка неудобочитаем: все его операторы - однобуквенные (так программисты понимали З тридцать лет тому назад... в чем-то они, наверное, были правы). Зато в нем операторов чуть больше дюжины, и все они подчиняются принципу аббревиативности (команда - первая буква i - i nsert). Из-за своей краткости sed - самая удобная утилита для работы в командной строке.
Поиск с заменой в sed выполняет команда s ( s earch). Синтаксис у нее такой: s/что_искать/на_что_заменять/. Не разбираясь пока в других командах, выполним обычную замену одной подстроки на другую. Для опытов изготовим файл из руководства по less (оно везде одинаково), обработав его уже известной нам командой colcrt:
$ man less | colcrt > dummy
Используем sed. Первый параметр - мини-сценарий, все остальные, если они - не ключи, представляют собой имена обрабатываемых файлов. Если неключевой параметр один, sed, как и многие утилиты UNIX, работает в режиме фильтра: читает со стандартного ввода и выводит на стандартный вывод. Заменим в файле dummy, скажем, file на и внимательно изучим получившийся текст из жизни насекомых:
$ sed 's/file/fly/' dummy | less . . .
Обратим внимание на строчку named flys has been viewed previously, the new files may be entered (можно, например, поискать командой / подстроку previously ). В этой sed заменил только первое найденное соответствие, причем сделал это, не разбирая, меняет ли он целое / в команде s модификатор g ( g lobal). Тогда, выполнив первую замену в sed продолжит искать оставшиеся в ней соответствия.
А вот со logfly или flyname выглядят вполне симпатично, но странное flys, образовавшееся из files, явно нуждается в доработке: по правилам английского языка множественное число от - flies. Это просто: никто не мешает выдать sed две команды; shell будет считать одним параметром все, что заключено между апострофами, переводы
$ sed 's/files/flies/g > s/file/fly/g' dummy
Стоит заметить, что в обратном порядке эти команды sed сработали бы по-другому: сначала команда 's/file/ заменила бы все file на , а 's/files/flies/g' работы вовсе не досталось бы.
Но, допустим, мы хотим заменять в этом тексте только file, а всевозможные profile и filename оставить как есть. Тут бы очень пригодились позиционные расширения РВ, отмечающие границы sed их нет, потому что их нет в sed обычно поддерживается некоторое приближение к file, окруженное небуквами, и заменять его. Возникает сразу три затруднения.
Первое: '[^a-z]file[^a-z]' соответствует подстрока из шести символов, поэтому команда 's/[^a-z]file[^a-z]/ наделает немало беспорядка, удалив по символу с каждой стороны от file. Требуется средство запомнить то, что было вокруг file, и вставить это вокруг . Иными sed, согласно правилам \( и \) ). Группы перенумерованы в порядке появления в РВ: группа, определяемая первой по счету открывающей скобкой, имеет номер 1, определяемая второй - 2 и т. п. Из найденной подстроки выделяются
фрагменты, соответствующие каждой группе (если группы вложены друг в друга, что допустимо, фрагменты могут пересекаться); эти фрагменты имеют ту же нумерацию, что и группы. Фрагмент можно использовать в строке-подстановке любое число раз (можно и не использовать) в виде конструкции \номер_фрагмента.
Задачу с file можно было бы решить так:
$ sed 's/\([^a-z]\)file\([^a-z]\)/\1fly\2/' dummy
При этом на место \1 подставится то, что найдено по первому '[^a-z]', а на место \2 - то, что найдено по второму.
Попробуем разобраться подробнее, как работает редактор sed. Входной текст sed считывает построчно. К считанной sed выводит на стандартный вывод то, что от нее осталось. Команда сценария может начинаться с т. н. $ (команда выполняется после закрытия входного потока) или 'sed "1s/_/ /g"' заменит в первой 'sed "/^a/d"' удалит ( d elete) все a.
Два 'sed "10,20d"' удалит 11 'sed "/if/,/fi/s/^/#/"' закомментирует все многострочные условные операторы в командном сценарии (если встретится однострочный, вида 'if.*fi', случится неприятность).
Мы обещали показать, как решить задачу с выделением GECOS из файла /etc/passwd при помощи поиска с заменой. Для этого нам понадобится еще одно свойство sed - ключ -n, с которым на стандартный вывод ничего не выводится, если не попросить специально. Попросить можно командой p ( p rint) или модификатором p команды s. Окончательное решение выглядит так (обе подстановки и присвоение мы для простоты опустим:
$ sed -n "/^max:/s/\([^:]*:\)\{4\}\([^:]*\).*/\2/p"
/etc/passwd
Max B. Tough
Перепишем выражение в расширенный формат, избавившись от лишних \:
/^max:/s/([^:]*:){4}([^:]*).*/\2/p
и разберемся. : только в конце (так выглядят все поля passwd, кроме последнего). '([^:]*:){4}' означает повторение такой посдстроки четыре раза, так что следом за ним идет как раз нужное нам пятое поле. '([^:]*)' помечает его как вторую группу. '.*' необходимо для того, чтобы s "съела" остаток '/\2/' означает "заменить на содержимое второй группы". И p в конце означает "выдать на стандартный вывод", потому что с ключом -n sed сам этого не делает. Просто, не правда ли?
Но вернемся к файлу dummy, в котором мы хотим заменять только целые file, и для этого проверяем, что перед шаблоном и после него стоят не буквы. Второе затруднение - в том, что перед шаблоном и после него может вообще ничего не стоять! Тогда наше решение не сработает, в чем легко убедиться, обработав его выдачу с помощью | grep 'file$'. Так что придется обработать еще три случая - '^file[a-z]', '[a-z]file$' и '^file$'.
Есть еще третье затруднение, самое неприятное. Непонятно, что делать со -file-file-file-.... Заменив первый -file- на -, sed ищет следующее после замены соответствие, которое начнется только с третьего -, и второе file будет, увы, пропущено. Средствами
Нам не пришлось бы ничего запоминать и восстанавливать, если бы в
Завершая описание sed, отметим, что во всех примерах в качестве выходного потока у нас фигурировал стандартный вывод. Если в командном интерпретаторе попробовать перенаправить вывод любого фильтра обратно в тот же файл, хорошего будет мало. Поэтому надо либо заводить временный файл вывода, а потом переименовывать его, либо пользоваться особым свойством некоторых фильтров: редактировать не поток, а файл, переписывая его только после окончания работы (т. н. in-place editing). Для этого в FreeBSD-версии у sed существует ключ -i, а в ALT Linux - отдельная команда subst.
Подробное изложение различных (действительно замечательная книга!).
На случай, когда средств редактора sed недостаточно, - а это бывает, если алгоритм изменения проекта сложнее простой замены, - в UNIX существует целый спектр более сложных инструментов, сочетающий обработку текста с программированием. Ближайший к sed - язык обработки текстов AWK, получивший название в честь фамилий авторов: Alfred V. Aho, Peter J. Weinberger, Brian W. Kernighan. В awk есть расширенное по отношению к sed понятие Си, есть переменные, работа с несколькими потоками данных и т. п. Вариант AWK, разрабатываемый GNU, - gawk - существенно дополняет стандартные возможности, но не изменяет идеологии.
Язык программирования Perl - сумма возможностей sed, awk, Си, дополненная мощными собственными инструментами. Perl называют "мечтой системного администратора" (или "мечтой лентяя", что одно и то же), потому что решение любой небольшой задачи на Perl можно написать очень быстро, коротко и совершенно нечитаемо для постороннего глаза. Впрочем, соблюдая дисциплину программирования, на Perl, как и на любом мощном языке, можно писать много и понятно. Дальше идут уже "классические" и "неоклассические" высокоуровневые языки программирования, вроде LISP или Python, в которых богатые диалекты
Одним из следствий У (точнее, правила
Формально текстовый файл - это файл, разбитый на \n ", ascii-код 12), в обработанном режиме именно
Вообще, каждый тип текстового файла имеет свои представления о лексике (правилах построения
В отличие от текста на естественном языке (например, на русском), тексты, составляющие различные проекты в UNIX, строго структурированы. Языки этих текстов имеют четкий синтаксис и довольно простую лексическую организацию. Если перед человеком стоит задача исправления проекта, для решения которой достаточно видоизменить имеющийся текст, а дописывать новые части проекта не надо, ее можно решать двумя способами: либо выбрасывать устаревшие куски и вписывать новые (что равносильно дописыванию), либо поручить эту работу роботу. Робот должен - на уровне, достаточном для данной работы, - разбираться в синтаксисе и лексике языка и иметь четкие инструкции, какие куски считать устаревшими и как их преобразовывать в новые.
Иными словами, стандартная задача обработки текста состоит из поиска фрагмента текста заданной структуры и преобразования этого фрагмента согласно заданному алгоритму. Таким способом изменять проект зачастую гораздо быстрее и уж точно не в пример интереснее, чем механически выполнять работу, которую может сделать робот. Типичный пример выполнимой задачи - поиск определенного
С задачей поиска помогает справиться мощный механизм представления структуры текста, называемый
Лексический анализ большинства языков в UNIX и большая часть синтаксического анализа делается именно на основе
Про
'регулярное выражение', а другие - так: строка.
Любой обычный символ - это f соответствует 'f'.
Спецсимвол . - M или @ соответствуют '.', аb или abc - не соответствуют (хотя abc содержит три фрагмента - a, b и c, соответствующих '.' ).
Конструкция из [ и ], между которыми находится один или несколько других символов, - это x и u соответствуют '[unix]', а U и 8 - не соответствуют. - ", который задает - ", и не больше, чем у стоящего справа. Например, любая буква латинского алфавита соответствует выражению '[a-zA-Z]'. Если нужно включить в - ", он должен идти сразу за [ или перед ]. Если при определении [ использовать [^, такому A и ; соответствуют выражению '[^aeiu-z]', а e и v - не соответствуют.
Последовательность '[1-9][0-9]': в числе, допустим, 36 3 соответствует '[1-9]', а 6 - '[0-9]'. Последовательность
*, - тоже 'a*' соответствуют подстроки a, aa, aaa и т. д. Выражению '[0-9]*' соответствует любая (включая пустую) последовательность десятичных цифр, например 1415926, потому что ее можно разбить на односимвольные фрагменты, каждый из котороых будет соответствовать выражению '[0-9]'. Наконец, выражению '(Foo)*' будут соответствовать подстроки Foo, FooFoo, FooFooFoo и т. д., но не будут соответствовать подстроки Fo, FFFoooooo, FFoo и Fwoo, так как их невозможно целиком разбить на фрагменты, соответствующие '(Foo)'. Любой подстроке любой длины соответствует выражение '.*'.
{минимум,максимум}, также '(he){2,4}' соответствуют подстроки hehe, hehehe и hehehehe. Если в 'Z{2,}' соответствуют подстроки ZZ, ZZZ, ZZZZ и т. д., а выражению 'w{,3}' - подстроки w, ww и www.
Спецсимволы !. Таким образом, выражению '^[a-z]{1,}$' будут соответствовать любые
Спецсимвол \ лишает следующий за ним любой спецсимвол специального значения (известная уже нам операция закавычивания). Например, выражению '\.\.\.' соответствует многоточие, а выражению '\$\*' - подстрока $*.
На этом описание
Операция выбора - .
Два частных случая повторителя-интервала: '{,1}' ), и +, обозначающий использование предшествующего '{1,}' ). Оба частных случая постоянно встречаются в повседневной работе; кроме того, обработка + во многих случаях идет существенно быстрее, чем обработка *, поэтому + стоит использовать вместо * везде, где только возможно.
В '[[:alnum:]]' (ему соответствует одна буква или одна цифра), или '[[:blank:]]' (ему соответствует один '[[:alpha:]]' или '[[:alnum:]]'. По этой же причине рекомендуется не использовать
| |
Соответствует | Не соответствует |
|---|---|---|
'a.c' |
abc, aWc, a+c |
ac, a..c, |
'F.*d' |
FddFd, Freud, F.*d |
feed, Sigmund, dF |
'u..[p-t]' |
user, u##s, uppp |
undo, uncut, u=t |
'wuff(-wuff)*' |
wuff, wuff-wuff-wuff |
wuffwuff, wuff- |
'[+-]?[0-9]+' |
+1, -0932, 333 |
+-7, -, 0+0 |
'[fit|plug](in|out)+' |
fitin, pluginoutin |
infit, plug, outin |
К $ и ^ добавлено еще два '[[:<:]]' и '[[:>:]]', которым соответствует начало и конец '[[:>:]]' зависит от символа, который не входит в подстроку-результат, т. е. зависит от контекста.
Новый синтаксис РВ получил название
Подстрока, соответствующая РВ, выбирается, как уже говорилось, из
Избавиться от неоднозначности при поиске подстроки в 'a.*d' в The syntax for word boundaries is incredibly ugly будет соответствовать подстрока от самого левого a до самого правого d: ax for word boundaries is incred.
Выражению 'B*' в BBcBBBd можно найти 17 соответствий (из них одна подстрока BBB, 3 BB, 5 B и 8 пустых подстрок в различных позициях). Правильным ответом будет BB в начале B и BB ). А вот в aBBcBBBd первой будет найдена пустая подстрока, потому что 'B*' имеет cоответствие в самом начале
Если некоторая подстрока может быть поставлена в соответствие '[0-9]+[0-9]+' в a123c5678d будет найдена подстрока 123, причем 12 будет соответствовать первому '[0-9]+', а 3 - второму.
Почему так? Во-первых, 5678 отпадает, потому что есть соответствие, которое лежит ближе к началу 123. Во-вторых, будет найдено именно 123, а не 1 или 12, потому что 123 - самое длинное из возможных соответствий. В-третьих, начальное '[0-9]+' и радо бы захватить для себя всю 123, но тогда на долю второго не остается ничего, и соответствия всему РВ не получается. Так что оно вынуждено уступить второму '[0-9]+' хотя бы один символ - 3.
Подобное поведение
Поиском подстрок в *grep - fgrep, grep и egrep. Первая из них - fgrep ( f ast grep) - не использует grep интерпретирует свой первый параметр как egrep работает с grep. Об имени ( фамилии?) самого семейства - grep - рассказано
в лекции 15.
Все утилиты семейства *grep распознают ключ -i ( i gnore case), при котором строчные и прописные буквы не различаются, ключ -v (in v ert), при котором выдаются -l, при котором выдаются только номера подходящих -q ( q uiet, в cтарых версиях grep - -s, s ilent), при котором вообще ничего не выводится, а команда используется ради кода ошибки, равного нулю, только когда шаблон найден. Кроме того, если в командной строке *grep всего один параметр, который не является ключом, этот параметр распознается как шаблон, а текст считывается со стандартного ввода; если же таких параметров несколько, то второй и последующие распознаются как имена файлов, и текст считывается из них. Наконец, если параметр-шаблон состоит из нескольких
Еще раз повторим, что задача grep - выбрать grep неважно, важно только, нашлось или нет. В таком виде эта утилита весьма полезна при работе с системой и применяется повсеместно; нам настолько тяжело было отказаться от нее в наших примерах, что пришлось без особых объяснений ввести ее еще в лекции 6 для ограничения контекста в команде .
Пример. Допустим, мы пишем сценарий на shell, в котором нам понадобится полное имя пользователя, запустившего этот сценарий. Полное имя пользователя (оно же GECOS) хранится в файле /etc/passwd. Для того чтобы извлечь его оттуда, надо знать входное имя пользователя, оно выдается командой logname. На всякий случай набираем в терминале
$ logname max $ egrep $(logname) /etc/passwd max:x:510:500:Max B. Tough:/home/max:/bin/sh maxim:x:541:500:Maxim Outsider:/home/maxim:/bin/sh sorrow:x:612:600:Temporary account:/home/shamaxe:/bin/sh
А вот и ошибка! Нас интересует входное имя, а не любая подстрока из :":
$ egrep "^$(logname):" /etc/passwd max:x:510:500:Max B. Tough:/home/max:/bin/sh
Теперь осталось выделить из этой cut, которая как раз предназначена для вывода некоторых полей. Напишем команду в виде, готовом для сценария (выполним еще одну подстановку и присвоим результат переменной GECOS):
$ GECOS=$(egrep "^$(logname):" /etc/passwd | cut -d: -f5) $ echo $GECOS Max B. Tough
Готово!
less либо more используется командой man, когда та показывает руководство, так что поиск в это время может очень пригодиться.
У less, кстати, есть одно незаметное для пользователя, но крайне полезное свойство. Показывая страницу помощи, less, как может, подкрашивает ее (подчеркиванием и ярким шрифтом). Так вот, операция поиска не зависит от того, какая часть найденной A+^H+A (напомним, что ^H - условное обозначение символа backspace (ascii-код 8, в восьмеричном виде - 10)), а подчеркнутая буква F - как _+^H+F. Однако, к счастью, команда /bn действительно находит bn в руководстве по less, хотя n выделена подчеркиванием. А grep без посторонней помощи не может справиться с такой задачей, приходится вызывать colcrt(1):
$ man less | colcrt | egrep bn
-bn or --buffers=n
Настало время подлить ложку дегтя в бочку меда, которой представляется '**' или '+?', и многие \ -последовательности ( '\n' - это n или перевод
В теоретическом подходе символы, управляющие разбором [ имеют специальное значение везде в РВ, а " ^ " и " $ " - соответственно в начале и в конце. Все остальные символы считаются обычными, а если они должны быть специальными, тогда перед ними надо поставить " \ ". В \( и \), а в \{ и \}. В таком виде grep, sed, more, vi и некоторые другие. Если программа использует библиотеку GNU \ ко всем спецсимволам, которые в grep, и sed, и more понимают шаблон 'etc\|bin' и исправно находят etc или bin:
linux$ ls -1 / | grep "etc\|bin" bin etc sbin
Наконец, когда программисты расширяют синтаксис \b (и начало \< и \> соответственно. Именно в Perl и pcre (perl compatible pcre(3) ). Похожие, тоже весьма мощные, диалекты PB используются в Python и некоторых других языках программирования. Собственный диалект
Таким образом, существует несколько
До этого мы все время говорили только об операции поиска подстроки в
Для поиска с заменой можно воспользоватья утилитой sed ( s tream ed itor - поточный редактор). Редактор sed весьма точно соответствует идеологии автоматического исправления проекта: это текстовый редактор, которым управляет не пользователь, а заданный sed -сценарий. Язык sed довольно своеобразен и слегка неудобочитаем: все его операторы - однобуквенные (так программисты понимали З тридцать лет тому назад... в чем-то они, наверное, были правы). Зато в нем операторов чуть больше дюжины, и все они подчиняются принципу аббревиативности (команда - первая буква i - i nsert). Из-за своей краткости sed - самая удобная утилита для работы в командной строке.
Поиск с заменой в sed выполняет команда s ( s earch). Синтаксис у нее такой: s/что_искать/на_что_заменять/. Не разбираясь пока в других командах, выполним обычную замену одной подстроки на другую. Для опытов изготовим файл из руководства по less (оно везде одинаково), обработав его уже известной нам командой colcrt:
$ man less | colcrt > dummy
Используем sed. Первый параметр - мини-сценарий, все остальные, если они - не ключи, представляют собой имена обрабатываемых файлов. Если неключевой параметр один, sed, как и многие утилиты UNIX, работает в режиме фильтра: читает со стандартного ввода и выводит на стандартный вывод. Заменим в файле dummy, скажем, file на и внимательно изучим получившийся текст из жизни насекомых:
$ sed 's/file/fly/' dummy | less . . .
Обратим внимание на строчку named flys has been viewed previously, the new files may be entered (можно, например, поискать командой / подстроку previously ). В этой sed заменил только первое найденное соответствие, причем сделал это, не разбирая, меняет ли он целое / в команде s модификатор g ( g lobal). Тогда, выполнив первую замену в sed продолжит искать оставшиеся в ней соответствия.
А вот со logfly или flyname выглядят вполне симпатично, но странное flys, образовавшееся из files, явно нуждается в доработке: по правилам английского языка множественное число от - flies. Это просто: никто не мешает выдать sed две команды; shell будет считать одним параметром все, что заключено между апострофами, переводы
$ sed 's/files/flies/g > s/file/fly/g' dummy
Стоит заметить, что в обратном порядке эти команды sed сработали бы по-другому: сначала команда 's/file/ заменила бы все file на , а 's/files/flies/g' работы вовсе не досталось бы.
Но, допустим, мы хотим заменять в этом тексте только file, а всевозможные profile и filename оставить как есть. Тут бы очень пригодились позиционные расширения РВ, отмечающие границы sed их нет, потому что их нет в sed обычно поддерживается некоторое приближение к file, окруженное небуквами, и заменять его. Возникает сразу три затруднения.
Первое: '[^a-z]file[^a-z]' соответствует подстрока из шести символов, поэтому команда 's/[^a-z]file[^a-z]/ наделает немало беспорядка, удалив по символу с каждой стороны от file. Требуется средство запомнить то, что было вокруг file, и вставить это вокруг . Иными sed, согласно правилам \( и \) ). Группы перенумерованы в порядке появления в РВ: группа, определяемая первой по счету открывающей скобкой, имеет номер 1, определяемая второй - 2 и т. п. Из найденной подстроки выделяются
фрагменты, соответствующие каждой группе (если группы вложены друг в друга, что допустимо, фрагменты могут пересекаться); эти фрагменты имеют ту же нумерацию, что и группы. Фрагмент можно использовать в строке-подстановке любое число раз (можно и не использовать) в виде конструкции \номер_фрагмента.
Задачу с file можно было бы решить так:
$ sed 's/\([^a-z]\)file\([^a-z]\)/\1fly\2/' dummy
При этом на место \1 подставится то, что найдено по первому '[^a-z]', а на место \2 - то, что найдено по второму.
Попробуем разобраться подробнее, как работает редактор sed. Входной текст sed считывает построчно. К считанной sed выводит на стандартный вывод то, что от нее осталось. Команда сценария может начинаться с т. н. $ (команда выполняется после закрытия входного потока) или 'sed "1s/_/ /g"' заменит в первой 'sed "/^a/d"' удалит ( d elete) все a.
Два 'sed "10,20d"' удалит 11 'sed "/if/,/fi/s/^/#/"' закомментирует все многострочные условные операторы в командном сценарии (если встретится однострочный, вида 'if.*fi', случится неприятность).
Мы обещали показать, как решить задачу с выделением GECOS из файла /etc/passwd при помощи поиска с заменой. Для этого нам понадобится еще одно свойство sed - ключ -n, с которым на стандартный вывод ничего не выводится, если не попросить специально. Попросить можно командой p ( p rint) или модификатором p команды s. Окончательное решение выглядит так (обе подстановки и присвоение мы для простоты опустим:
$ sed -n "/^max:/s/\([^:]*:\)\{4\}\([^:]*\).*/\2/p"
/etc/passwd
Max B. Tough
Перепишем выражение в расширенный формат, избавившись от лишних \:
/^max:/s/([^:]*:){4}([^:]*).*/\2/p
и разберемся. : только в конце (так выглядят все поля passwd, кроме последнего). '([^:]*:){4}' означает повторение такой посдстроки четыре раза, так что следом за ним идет как раз нужное нам пятое поле. '([^:]*)' помечает его как вторую группу. '.*' необходимо для того, чтобы s "съела" остаток '/\2/' означает "заменить на содержимое второй группы". И p в конце означает "выдать на стандартный вывод", потому что с ключом -n sed сам этого не делает. Просто, не правда ли?
Но вернемся к файлу dummy, в котором мы хотим заменять только целые file, и для этого проверяем, что перед шаблоном и после него стоят не буквы. Второе затруднение - в том, что перед шаблоном и после него может вообще ничего не стоять! Тогда наше решение не сработает, в чем легко убедиться, обработав его выдачу с помощью | grep 'file$'. Так что придется обработать еще три случая - '^file[a-z]', '[a-z]file$' и '^file$'.
Есть еще третье затруднение, самое неприятное. Непонятно, что делать со -file-file-file-.... Заменив первый -file- на -, sed ищет следующее после замены соответствие, которое начнется только с третьего -, и второе file будет, увы, пропущено. Средствами
Нам не пришлось бы ничего запоминать и восстанавливать, если бы в
Завершая описание sed, отметим, что во всех примерах в качестве выходного потока у нас фигурировал стандартный вывод. Если в командном интерпретаторе попробовать перенаправить вывод любого фильтра обратно в тот же файл, хорошего будет мало. Поэтому надо либо заводить временный файл вывода, а потом переименовывать его, либо пользоваться особым свойством некоторых фильтров: редактировать не поток, а файл, переписывая его только после окончания работы (т. н. in-place editing). Для этого в FreeBSD-версии у sed существует ключ -i, а в ALT Linux - отдельная команда subst.
Подробное изложение различных (действительно замечательная книга!).
На случай, когда средств редактора sed недостаточно, - а это бывает, если алгоритм изменения проекта сложнее простой замены, - в UNIX существует целый спектр более сложных инструментов, сочетающий обработку текста с программированием. Ближайший к sed - язык обработки текстов AWK, получивший название в честь фамилий авторов: Alfred V. Aho, Peter J. Weinberger, Brian W. Kernighan. В awk есть расширенное по отношению к sed понятие Си, есть переменные, работа с несколькими потоками данных и т. п. Вариант AWK, разрабатываемый GNU, - gawk - существенно дополняет стандартные возможности, но не изменяет идеологии.
Язык программирования Perl - сумма возможностей sed, awk, Си, дополненная мощными собственными инструментами. Perl называют "мечтой системного администратора" (или "мечтой лентяя", что одно и то же), потому что решение любой небольшой задачи на Perl можно написать очень быстро, коротко и совершенно нечитаемо для постороннего глаза. Впрочем, соблюдая дисциплину программирования, на Perl, как и на любом мощном языке, можно писать много и понятно. Дальше идут уже "классические" и "неоклассические" высокоуровневые языки программирования, вроде LISP или Python, в которых богатые диалекты
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.