Начало работы с командой SED [Руководство для начинающих]
Научитесь использовать одну из самых мощных команд набора инструментов Unix: sed, потоковый редактор с практическими примерами команд SED.
Sed является частью стандартного набора инструментов Unix с конца 60-х годов. Как и любой текстовый редактор, он поможет вам редактировать текстовые файлы. Однако, в отличие от текстовых редакторов, которые вы, возможно, уже использовали, он неинтерактивный.
Это означает, что вы заранее указываете преобразования, которые хотите применить к файлу, а затем инструмент может применить эти преобразования без присмотра.
Лучшее описание целей разработки инструмента дал Ли Э. МакМэхон, основной разработчик исходной реализации, в своей оригинальной статье sed:
Sed — это неинтерактивный контекстный редактор, работающий в операционной системе UNIX. Sed особенно полезен в трёх случаях:
-
Редактировать файлы, слишком большие для удобного интерактивного редактирования;
Для редактирования файла любого размера, когда последовательность команд редактирования слишком сложна для удобного ввода в интерактивном режиме.
Для эффективного выполнения нескольких «глобальных» функций редактирования за один проход через вход.
Целевые конструкции (1) и (3), вероятно, менее актуальны для нашего современного оборудования, но второй остается в силе. В качестве личного дополнения я бы сказал, что sed особенно хорошо подходит для повторяющихся задач, например, когда вы хотите применить одно и то же преобразование к набору файлов.
Изучите основные команды SED на этих примерах.
Чтобы дать вам представление о возможностях sed, я рассмотрю случай разработчика, которому необходимо добавить заголовок лицензии поверх каждого исходного файла в своем проекте:
linux@handbook:~$ head MIT.LICENSE *.sh
==> MIT.LICENSE <==
-----8<----------------------------------------------------------------
Copyright <YEAR> <COPYRIGHT HOLDER>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
==> script1.sh <==
#!/bin/bash
echo Hello, I\'m the first script
==> script2.sh <==
#!/bin/bash
cat << EOF
Hello, I'm the second script
EOF
Я не только хотел бы видеть файл лицензии поверх каждого сценария оболочки, но также хотел бы, чтобы год и заполнитель авторских прав были заменены их фактическим значением. Это будет наш первый вариант использования.
Примечание. Если вы хотите попрактиковаться самостоятельно, вы можете скачать файлы примеров с моего сайта. Вы также можете посмотреть видео, дополняющее эту статью:
1. Замена текста в СЭД
В моем файле лицензии я хотел бы заменить заполнители
Эта задача идеально подходит для команды sed substitution. Вероятно, самая полезная из всех команд sed:
linux@handbook:~$ sed -e 's/<YEAR>/2018/' MIT.LICENSE | head -5
-----8<----------------------------------------------------------------
Copyright 2018 <COPYRIGHT HOLDER>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Используя канал (|
), я перенаправил вывод команды sed в инструмент head
, чтобы отобразить здесь только первые пять строк. Однако для нашей сегодняшней конкретной темы наиболее интересной частью является выражение s/
.
Sed работает, обрабатывая входной файл по одной строке за раз. В каждой строке команда замены (s
) заменит первое вхождение текста между первыми двумя косыми чертами (/
) на текст между последними два (/2018/
). Думайте об этом как о функции поиска и замены, которая есть в текстовом редакторе с графическим интерфейсом.
Здесь стоит отметить, что исходный файл MIT.LICENSE не был изменен. Я позволю вам проверить это самостоятельно, используя следующую команду:
head -5 MIT.LICENSE
2. Замена текста… еще раз
Отлично: мы заменили заполнитель года. Но есть второй на замену. Если вы поняли предыдущий пример, вы, вероятно, могли бы представить себе второе выражение sed, подобное этому:
's/<COPYRIGHT HOLDER>/Sylvain Leroux/'
Но где это разместить? Что ж, у вас есть несколько вариантов. Самый очевидный вариант, если вы уже знакомы с концепцией перенаправления, — это передать вывод нашей первой команды sed во второй экземпляр sed:
linux@handbook:~$ sed -e 's/<YEAR>/2018/' MIT.LICENSE |
sed -e 's/<COPYRIGHT HOLDER>/Sylvain Leroux/' |
head -5
----8<----------------------------------------------------------------
Copyright 2018 Sylvain Leroux
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
Но мы можем добиться большего. Поскольку опция -e
вводит выражение sed, мы можем использовать несколько из них как часть одного и того же вызова sed, и результат будет таким же:
# Pay special attention to the \ at the end of the lines
# specifying the *same* command continues on the
# next line:
sh$ sed -e 's/<YEAR>/2018/' \
-e 's/<COPYRIGHT HOLDER>/Sylvain Leroux/' \
MIT.LICENSE |
head -5
Наконец, вы также можете указать несколько команд в одном выражении sed, разделив их новой строкой. Это особенно полезно, когда вы начинаете писать более сложные программы sed:
# Pay special attention to the single-quotes and
# backslash placement:
sh$ sed -e 's/<YEAR>/2018/
s/<COPYRIGHT HOLDER>/Sylvain Leroux/' \
MIT.LICENSE |
head -5
3. Вставка текста
Теперь мы заменили заполнители их фактическим значением. Но нам еще предстоит проделать некоторую работу, прежде чем мы сможем вставить этот файл лицензии в файлы проекта. Те, которые позже являются сценариями оболочки, каждая строка лицензии должна начинаться с октотора (#
), чтобы оболочка понимала, что она не должна пытаться интерпретировать эти строки.
Для этого мы снова воспользуемся командой подстановки. Я не упомянул ранее то, что, в отличие от большинства функций замены поиска в редакторах с графическим интерфейсом, шаблон поиска не обязательно представляет собой буквальную строку для поиска. По сути, это регулярное выражение (regex). Это означает, что помимо простых символов, которые будут совпадать дословно, вы можете использовать символы, имеющие особое значение. Например, курсор (^
) обозначает начало строки, знак доллара ($
) — конец строки или, как в последнем примере, точка. -star (.*
) означает любую последовательность из 0, 1 или нескольких символов. Есть много других подобных метасимволов, но на данный момент этого более чем достаточно.
Таким образом, чтобы вставить некоторый текст в начало строки, можно заменить начало строки по этому тексту:
linux@handbook:~$ sed -e 's/<YEAR>/2018/' \
-e 's/<COPYRIGHT HOLDER>/Sylvain Leroux/' \
-e 's/^/# /' \
MIT.LICENSE | head -5
# -----8<----------------------------------------------------------------
# Copyright 2018 Sylvain Leroux
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
4. Очистка выбранных строк
Команда подстановки в sed настолько универсальна, что с ее помощью можно выполнить большинство преобразований текста. Например, чтобы удалить пунктирные линии сверху и снизу текста лицензии, я мог бы написать следующее:
linux@handbook:~$ sed -e 's/<YEAR>/2018/' \
-e 's/<COPYRIGHT HOLDER>/Sylvain Leroux/' \
-e 's/^/# /' \
-e 's/^.*----.*$//' \
MIT.LICENSE | head -5
# Copyright 2018 Sylvain Leroux
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
Эта более поздняя замена заменила пустой строкой весь текст:
- ^
Начиная с начала строки
- .*
За которым следует любая последовательность из 0, 1 или нескольких символов.
- ----
Далее следуют 4 дефиса
- .*
За которым следует любая последовательность из 0, 1 или нескольких символов.
- $
Далее следует конец строки
Короче говоря, это заменит всю строку пустой строкой, если она содержит четыре дефиса подряд. Но сама пустая строка остается в выводе и отображается как пустая строка.
В зависимости от ваших конкретных потребностей и вкусов вы также можете рассмотреть альтернативное решение, представленное ниже. Я позволю вам изучить это подробно, чтобы точно определить изменения в команде и самостоятельно определить, каковы были последствия для результата:
linux@handbook:~$ sed -e 's/<YEAR>/2018/' \
-e 's/<COPYRIGHT HOLDER>/Sylvain Leroux/' \
-e 's/^.*----.*$//' \
-e 's/^/# /' \
MIT.LICENSE | head -5
Если вы обнаружите, что регулярное выражение, используемое для очистки строки, слишком сложное, мы также можем воспользоваться другой функцией sed. Почти все команды могут иметь необязательный адрес перед именем команды. Если он присутствует, он ограничит область действия команды строками, соответствующими этому адресу:
linux@handbook:~$ sed -e 's/<YEAR>/2018/' \
-e 's/<COPYRIGHT HOLDER>/Sylvain Leroux/' \
-e 's/^/# /' \
-e '/----/s/^.*$//' \
MIT.LICENSE | head -5
Теперь последняя команда замены будет применяться только к строкам, соответствующим (т. е. «содержащим») четырем тире подряд. И для каждой совпадающей строки он заменит все (.*
) между началом (^
) и концом ($
) строки на пустая строка (//
)
5. Удаление выделенных строк
В предыдущем разделе мы изменили команду замены, чтобы очистить некоторые строки текста. Но пустые строки остались. Иногда это желательно. Иногда это не так. В последнем случае вам может потребоваться изучить команду delete, чтобы удалить целые строки из вывода:
# Below, the redirection '> LICENSE' is used to store
# the result of the sed command into the newly
# created LICENSE file:
linux@handbook:~$ sed -e 's/<YEAR>/2018/' \
-e 's/<COPYRIGHT HOLDER>/Sylvain Leroux/' \
-e 's/^/# /' \
-e '/----/d' \
MIT.LICENSE > LICENSE
linux@handbook:~$ head -5 LICENSE
# Copyright 2018 Sylvain Leroux
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
d
— это имя команды delete. Точно так же, как s
было именем команды подстановки. Здесь мы указали адрес перед командой, поэтому будут удалены только совпадающие строки (без адреса команда d
удалила бы каждую строку файла).
6. Преобразование в верхний регистр
До сих пор мы фокусировались главным образом на верхней части файла лицензии. Но действительно есть некоторые изменения, которые я хотел бы внести в документы немного дальше. Давайте сначала разберемся, о чем я говорю:
linux@handbook:~$ sed -ne '/The above/,$p' LICENSE
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# The software is provided "as is", without warranty of any kind,
# express or implied, including but not limited to the warranties of
# merchantability, fitness for a particular purpose and noninfringement.
# In no event shall the authors or copyright holders be liable for any
# claim, damages or other liability, whether in an action of contract,
# tort or otherwise, arising from, out of or in connection with the
# software or the use or other dealings in the software.
В приведенной выше команде с помощью опции -n я отключил автоматическую печать пространства шаблонов. Это означает, что sed больше не будет ничего печатать на выходе, если я явно не попрошу его об этом. Именно это я и делаю с помощью команды print (p). Обратите внимание, что вместо использования одного адреса перед командой p я использовал диапазон для отображения текста между строкой, содержащей текст «Выше» и концом документа. ($).
Команда печати может быть полезна, когда вам нужно извлечь некоторые части файла. Однако на сегодняшний день я просто хотел показать последние два абзаца, чтобы объяснить, что мне нужно сейчас: поскольку это традиция с файлами лицензий, я хотел бы обезопасить себя, дав понять, что программное обеспечение предоставляется «как есть». Поэтому я хотел бы подчеркнуть последний абзац (начиная с «Программное обеспечение»), переписав его в верхнем регистре.
В заменяющей части команды замены знак & заменяется текстом, соответствующим шаблону поиска. Используя расширение GNU \U, мы можем изменить регистр замещающей строки:
linux@handbook:~$ sed -i -e '/The software/,$s/.*/\U&/' LICENSE
linux@handbook:~$ cat LICENSE
В обычном тексте s/.*/\U&/
означает «заменить любой текст (.*
) версией в верхнем регистре (\U
)». сам (&
). Позвольте вам убедиться сами: последний абзац теперь должен быть написан заглавными буквами. Кстати, вы могли заметить, что из-за флага -i
изменения были применены непосредственно к файлу LICENSE.
Мы увидим это более подробно в следующем разделе. А пока я позволяю вам практиковаться и изменять эти команды по вашему желанию. Если у вас есть файл лицензии, соответствующий вашему вкусу, пришло время посмотреть, как включать его в каждый исходный файл проекта.
7. Вставка текстового файла
Если вы ожидаете здесь какой-то сложной команды, вы будете разочарованы: вставить файл в другой довольно просто:
sed -i -e '1r LICENSE' script1.sh
cat script1.sh
Здесь стоит увидеть две вещи:
выражение
r LICENSE
— это команда для чтения и вставки внешнего файла в файл, обрабатываемый в данный момент. Здесь перед ним стоит число1
, которое представляет собой адрес, соответствующий только строке 1 входного файла.опция
-i
позволяет изменить файл на месте. Это означает, что sed создаст за сценой временный файл для хранения там своих результатов, а после завершения обработки заменит исходный файл модифицированным.
Интересный побочный эффект опции «-i» заключается в том, что вы можете указать несколько имен файлов в командной строке, и sed будет применять одни и те же преобразования к каждому из них независимо:
sed -i -e '1r LICENSE' *.sh
8. Назад в будущее
В качестве нашего последнего примера команды sed представим, что прошло несколько лет, и сейчас наступило 1 января 2024 года. Уведомление об авторских правах для всех файлов должно быть обновлено. Существует несколько вариантов использования в зависимости от того, когда были созданы файлы проекта. Итак, наши уведомления об авторских правах должны иметь один из этих двух форматов:
- Copyright 2023
Для файлов, созданных в прошлом году
- Copyright 2018-2023
Для файлов, созданных до прошлого года
Мы можем охватить эти два варианта использования одновременно, используя расширенное (-E) регулярное выражение. Единственные «расширенные» вещи, которые мы здесь будем использовать, — это круглые скобки:
sed -i -Ee 's/Copyright (....)(-....)?/Copyright \1-2024/' *.sh
Я рекомендую вам вручную изменить уведомление об авторских правах в файлах *.sh, а затем запустить приведенную выше команду в различных случаях использования, чтобы увидеть, как она работает.
Возможно, в конечном итоге это поможет вам понять, если я скажу в шаблоне поиска: Copyright:: — это буквальный текст, который будет соответствовать дословно; (… .):: определяет группу захвата, соответствующую четырем произвольным символам. Надеюсь, четыре цифры года; (-… .)?:: определяет группу захвата, соответствующую тире, за которым следуют четыре произвольных символа. Знак вопроса в конце означает, что группа не является обязательной. Он может присутствовать, а может и не присутствовать во входной строке.
В строке замены: Copyright:: — это буквальный текст, который будет скопирован дословно; \1:: — это содержимое первой группы захвата. -2024:: — это буквальный текст, который будет скопирован дословно.
Если вы нашли время проверить команду самостоятельно, она должна подтвердить, что если я применю эти правила к случаям использования, описанным в предыдущей таблице, я получу что-то вроде этого:
Matching text | \1 | \2 | Replacement string |
---|---|---|---|
Copyright 2023 | 2023 | Copyright 2023-2024 | |
Copyright 2018-2023 | 2018 | -2023 | Copyright 2018-2024 |
В завершение нашего руководства по SED
Здесь мы лишь коснулись поверхности. Инструмент sed
гораздо более мощный. Однако даже если мы видели только четыре команды (s
, p
, d
и i
) и несколько базовых конструкций регулярных выражений (^
, $
, .
, ?
и .*
), у вас уже достаточно знаний, чтобы решить многие повседневные проблемы.
Поскольку мне нравится заканчивать руководство небольшим испытанием, вот что я вам предлагаю: если вы загрузили вспомогательные материалы, вы найдете в каталоге проекта файл с именем hello.c
. Это исходный файл базовой программы на языке C:
linux@handbook:~$ ls
hello.c MIT.LICENSE script1.sh script2.sh
linux@handbook:~$ gcc hello.c -o hello
linux@handbook:~$ ./hello sylvain
Hello sylvain
linux@handbook:~$ cat hello.c
В исходном файле уже есть некоторые комментарии. Используя их в качестве примеров синтаксиса комментариев на языке программирования C, можете ли вы вставить лицензию MIT в исходный файл hello.c
с помощью команды sed? Вы можете использовать одну или несколько команд sed, вы можете перенаправить вывод команды sed в другую, вы можете использовать временные файлы, если хотите, но вам не разрешено использовать любую другую команду, кроме sed. Конечно, исходный файл C все равно должен скомпилироваться после того, как вы вставите лицензию!
Теперь я позволю вам подумать об этой маленькой проблеме и надеюсь, что вам понравилась эта статья и сопутствующее видео. Если вы хотите узнать больше о sed, сообщите нам об этом в разделе комментариев!