Краткая информация: научитесь использовать одну из самых мощных команд инструментария Unix: SED, редактор потока с практическими примерами команд SED.
Sed является частью стандартного инструментария Unix с конца 60-х годов. Как любой текстовый редактор, он поможет вам изменить текстовые файлы. Однако, в отличие от текстовых редакторов, которые вы, возможно, уже использовали, этот неинтерактивный. Это означает, что вы заранее указываете преобразования, которые хотите применить к файлу, а затем инструмент может применять эти преобразования без контроля.
Лучшее описание целей дизайна инструмента принадлежит Ли Э. МакМахону, основному разработчику оригинальной реализации в его оригинальной сборнике :
Sed – неинтерактивный редактор контекста, который работает в операционной системе UNIX. Sed разработан, чтобы быть особенно полезным в трех случаях:
Цели проекта (1) и (3), вероятно, менее актуальны для нашего современного оборудования, но второй остается в силе. В качестве личного дополнения я бы сказал, что sed особенно хорошо подходит для повторяющихся задач, например, когда вы хотите применить одно и то же преобразование к набору файлов.
Чтобы дать вам представление о силе sed, я рассмотрю случай разработчика, которому необходимо добавить заголовок лицензии поверх каждого из исходных файлов в ее проекте:
sh$ head MIT.LICENSE *.sh ==> MIT.LICENSE <== -----8<---------------------------------------------------------------- Copyright 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
Я не только хотел бы видеть файл лицензии поверх каждого сценария оболочки, но также хотел бы, чтобы год и место для замещения авторских прав заменялись их фактическим значением. Это будет наш первый случай использования.
В моем файле лицензии я хотел бы заменить метки и на их фактическое значение.
Это работа, идеально подходящая для команды замены sed. Вероятно, самая полезная из всех команд sed:
sh$ sed -e 's//2018/' MIT.LICENSE | head -5 -----8<---------------------------------------------------------------- Copyright 2018 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the
Используя pipe ( |
), я перенаправил вывод команды sed в инструмент head
, чтобы отображать здесь только первые пять строк. Однако для нашей сегодняшней конкретной темы наиболее интересной является выражение s//2018/
.
Sed работает, обрабатывая входной файл по одной строке за раз. В каждой строке команда substitute ( s
) заменяет первое вхождение текста между двумя двумя слэшами ( //
) текстом между двумя последними ( /2018/
). Подумайте об этом, как функция поиска-замены, которую вы используете в текстовом редакторе графического интерфейса.
Стоит упомянуть здесь, исходный файл MIT.LICENSE не был изменен. Я позволю вам проверить это самостоятельно, используя следующую команду:
head -5 MIT.LICENSE
Отлично: мы заменили год. Но есть вторая, которую нужно заменить. Если вы поняли предыдущий пример, вы, вероятно, могли бы представить второе выражение sed, подобное этому:
's//AndreyEx/'
Но куда это поместить? У вас есть несколько вариантов. Наиболее очевидным, если вы уже знакомы с концепцией перенаправления, является передача вывода нашей первой команды sed ко второму экземпляру sed:
sh$ sed -e 's//2018/' MIT.LICENSE | sed -e 's//AndreyEx/' | head -5 ----8<---------------------------------------------------------------- Copyright 2018 AndreyEx 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//2018/' \ -e 's//AndreyEx/' \ MIT.LICENSE | head -5
Наконец, вы также можете указать несколько команд в одном и том же выражении sed, разделив их на новую строку. Это особенно полезно, когда вы начинаете писать более сложные программы sed:
# Pay special attention to the single-quotes and # backslash placement: sh$ sed -e 's//2018/ s//AndreyEx/' \ MIT.LICENSE | head -5
Теперь мы заменили заполнители их фактической стоимостью. Но нам еще предстоит проделать определенную работу, прежде чем вставлять этот файл лицензии в файлы проекта. Те, кто позже являются сценариями оболочки, каждая строка лицензии должна начинаться с octothorp ( #
), чтобы оболочка поняла, что не должна пытаться интерпретировать эти строки.
Для этого мы снова будем использовать команду подстановки. Что-то, о чем я не упоминал ранее, вопреки большинству функций замены для графических редакторов, шаблон поиска не обязательно является литеральной строкой для поиска. На самом деле это регулярное выражение (регулярное выражение) . Это означает, что в дополнение к простым символам, которые будут соответствовать дословно, вы можете использовать символы, которые будут иметь особое значение. Например, карет ( ^
) представляет начало строки, знак доллара ( $
) конец строки или, как последний пример, точка-звезда ( .*
) означает любую последовательность из 0, 1 или нескольких символов. Есть много других подобных метасимволов, но пока этого более чем достаточно.
Таким образом, чтобы вставить текст в начале строки, необходимо заменить начало строки на этот текст:
sh$ sed -e 's//2018/' \ -e 's//AndreyEx/' \ -e 's/^/# /' \ MIT.LICENSE | head -5 # -----8<---------------------------------------------------------------- # Copyright 2018 AndreyEx # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the
Команда подстановки в sed настолько универсальна, что вы можете выразить большую часть текстовых преобразований, используя ее. Например, чтобы удалить пунктирные строки сверху и снизу текста лицензии, я мог бы написать следующее:
sh$ sed -e 's//2018/' \ -e 's//AndreyEx/' \ -e 's/^/# /' \ -e 's/^.*----.*$//' \ MIT.LICENSE | head -5 # Copyright 2018 AndreyEx # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the
Эта последующая замена заменила пустой строкой весь текст:
^
начиная с начала строки.*
за которым следует любая последовательность из 0, 1 или нескольких символов----
затем четыре дефиса.*
за которым следует любая последовательность из 0, 1 или нескольких символов$
за которым следует конец строки.Короче говоря, это заменит всю строку пустой строкой, если она содержит четыре штриха в строке. Но сама пустая строка остается на выходе и будет отображаться как пустая строка.
В зависимости от ваших конкретных потребностей и вкусов вы также можете рассмотреть альтернативное решение ниже. Я позволил вам подробно изучить это, чтобы точно определить изменения в команде и определить сами, каковы были последствия для результата:
sh$ sed -e 's//2018/' \ -e 's//AndreyEx/' \ -e 's/^.*----.*$//' \ -e 's/^/# /' \ MIT.LICENSE | head -5
Если вы обнаружите, что регулярное выражение используется для очистки строки немного сложнее, мы также можем воспользоваться другой функцией sed. Почти все команды могут принимать необязательный адрес перед именем команды. Если он присутствует, он ограничивает объем команды строками, соответствующими этому адресу:
sh$ sed -e 's//2018/' \ -e 's//AndreyEx/' \ -e 's/^/# /' \ -e '/----/s/^.*$//' \ MIT.LICENSE | head -5
Теперь последняя команда подстановки будет применена только к строкам, сопоставляющим (т. е. «Содержащий») четыре штриха в строке. И для каждой подходящей строки он заменит все ( .*
) между start ( ^
) и end ( $
) строки пустой строкой ( //
)
В предыдущем разделе мы изменили команду подстановки, чтобы очистить некоторые строки текста. Но пустые строки остались. Иногда это желательно. Иногда это не так. В этом последнем случае вам может понадобиться изучить команду delete, чтобы удалить целые строки из вывода:
# Below, the redirection '> LICENSE' is used to store # the result of the sed command into the newly # created LICENSE file: sh$ sed -e 's//2018/' \ -e 's//AndreyEx/' \ -e 's/^/# /' \ -e '/----/d' \ MIT.LICENSE > LICENSE sh$ head -5 LICENSE # Copyright 2018 AndreyEx # # 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
была имени команды substitution. Здесь мы указали адрес перед командой, так что будут удалены только соответствующие строки (без какого-либо адреса команда d
удалила бы каждую строку файла)
До сих пор мы фокусировались главным образом на верхней части файла лицензии. Но действительно есть некоторые изменения, которые я хотел бы выполнить немного дальше в документах. Давайте сначала посмотрим, что мы говорим:
sh$ 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, я использовал диапазон для отображения текста между строкой, содержащей текст «Выше» и конец документа ($).
Команда print может быть полезна, когда вам нужно извлечь некоторые части файла. Однако на сегодняшний день я просто хотел отобразить последние два абзаца, чтобы объяснить, что мне нужно сейчас: поскольку это традиция с файлами лицензий, я хотел бы покрыть себя, прояснив, что программное обеспечение предоставляется «как есть». Поэтому я хотел бы обратить внимание на последний абзац (начиная с «Программного обеспечения»), переписав его заглавными буквами.
В заменяющей части команды подстановки символ & заменяется текстом, соответствующим шаблону поиска. Используя расширение \ U GNU, мы можем изменить случай замены строки:
sh$ sed -i -e '/The software/,$s/.*/\U&/' LICENSE sh$ cat LICENSE
В простом тексте s/.*/\U&/
означает «заменить любой текст ( .*
) самой заглавной ( \U
) версией самой ( &
). Я позволяю вам проверять самостоятельно, последний абзац теперь должен быть написан во всех прописных. Кстати, вы, возможно, заметили из-за флага -i
, изменения были применены непосредственно к файлу LICENSE.
Мы увидим это более подробно в следующем разделе. Тем временем я позволяю вам практиковать и изменять эти команды по своему желанию. После того, как у вас есть файл лицензии, соответствующий вашему вкусу, пришло время посмотреть, как включить его перед каждым исходным файлом проекта.
Если вы ожидаете здесь сложную команду, вы будете разочарованы: вставка файла в другой довольно проста:
sed -i -e '1r LICENSE' script1.sh cat script1.sh
Здесь можно увидеть две вещи:
r LICENSE
является командой для чтения и вставьте внешний файл в файл в настоящее время обрабатывается. Здесь префикс содержит число, 1
которое является адресом, соответствующим только строке 1 входного файла.-i
позволяет изменять файл на месте . Это означает, что sed создаст временный файл за сценой, чтобы сохранить там свой вывод, и, как только обработка завершится, он заменит исходный файл на измененный.Интересным побочным эффектом опции -i является то, что вы можете указать несколько имен файлов в командной строке, а sed будет применять одни и те же преобразования к каждому из них независимо :
sed -i -e '1r LICENSE' *.sh
Как наш последний пример команды sed, давайте представим, что прошло несколько лет, и у нас 1 января 2024 года. Уведомление об авторских правах для всех файлов должно быть обновлено. Существует несколько вариантов использования, в зависимости от того, когда были созданы файлы проекта. Итак, наши уведомления об авторских правах должны следовать одному из этих двух форматов:
Текущие авторские права | Описание |
---|---|
Copyright 2023 | Для файлов, созданных в прошлом году |
Copyright 2018-2023 | Для файлов, созданных до прошлого года |
Мы можем захватить эти два варианта использования сразу с использованием расширенного регулярного выражения (-E). Единственные «расширенные» вещи, которые мы действительно будем использовать здесь, – это скобки:
sh$ sed -i -Ee 's/Copyright (....)(-....)?/Copyright \1-2024/' *.sh
Я рекомендую вам вручную изменить уведомление об авторских правах в файлах * .sh, а затем запустить приведенную выше команду в случаях использования, чтобы увидеть, как она работает.
В конечном итоге это может помочь вам понять, если я скажу в шаблоне поиска: Copyright :: – это буквальный текст, который будет соответствовать дословно; (….) :: определяет группу захвата, соответствующую четырем произвольным символам. Надеюсь, четыре цифры года; (- ….)? :: определяет группу захвата, соответствующую тире, за которой следуют четыре произвольных символа. Значок вопроса в конце указывает, что группа является необязательной. Он может или не может присутствовать во входной строке.
В строке замены: Copyright :: – это буквальный текст, который будет скопирован дословно; \ 1 :: является содержимым первой группы захвата -2024 :: является буквальным текстом, который будет скопирован дословно.
Если вы потратили время на проверку команды самостоятельно, она должна подтвердить, применяю ли эти правила к примерам использования, описанным в предыдущей таблице, я получу что-то вроде этого:
Соответствующий текс | \1 | \2 | Сменная строка |
---|---|---|---|
Copyright 2023 | 2023 | Copyright 2023-2024 | |
Copyright 2018-2023 | 2018 | -2023 | Copyright 2018-2024 |
Мы только затронули команду здесь. Инструмент sed
является гораздо более мощным, чем описано здесь. Однако, даже если мы видели только четыре команды ( s
, p
, d
и i
) и несколько основных регулярных выражений конструкции ( ^
, $
, .
, ?
и .*
), у вас уже есть достаточно знаний, мы уже можем решить много проблем.
Если вы загрузили материал поддержки, вы найдете в каталоге проекта файл с именем hello.c
. Это исходный файл базовой программы на C:
sh$ ls hello.c MIT.LICENSE script1.sh script2.sh sh$ gcc hello.c -o hello sh$ ./hello sylvain Hello sylvain sh$ cat hello.c
В исходном файле уже есть комментарии. Используя их в качестве примеров синтаксиса комментариев на языке программирования C, можете ли вы вставить лицензию MIT в исходный файл hello.c
с помощью команды sed? Вы можете использовать одну или несколько команд sed, вы можете передать вывод команды sed в другую, вы можете использовать временные файлы, если хотите, но вам не разрешено использовать какую-либо другую команду, кроме sed. Конечно, исходный файл C должен компилироваться после того, как вы вставили лицензию!
Если вы хотите узнать больше о sed, сообщите нам об этом, используя раздел комментариев!