Хотя sed иногда кажется загадочным, как только вы разберетесь в синтаксисе, это легко

Sed, что означает редактор потока, вероятно, наиболее часто используется для поиска и замены строк в сценариях bash. Что-то вроде этого:

sed 's/foo/bar/g' foo.txt

Это мощный инструмент, но он немного сбивает с толку. Для меня на первый взгляд sed выглядел так же запутанно, как и регулярное выражение до того, как я его узнал - просто набор символов и символов, случайно смешанных вместе. Sed на самом деле довольно прост, если вы знаете его простой синтаксис. После изучения он позволяет вам выполнять мощные функции, такие как программная замена, удаление или вставка текста в файл или поток.

Как работает команда

Сама команда sed проста и имеет следующую форму:

sed [options/flags...] [sed script] [input file(s)]

Обратите внимание, что вы обычно хотите заключить то, что вы передаете в sed, в одинарные кавычки. Это не позволяет bash интерпретировать содержимое и заменять специальные символы. Кроме того, как мы увидим позже, macOS sed и GNU / Linux sed отличаются. macOS sed довольно старый, поэтому он не настолько эффективен. Таким образом, только GNU sed может обрабатывать несколько входных файлов или глобусов.

Как работает синтаксис

Вот как работает синтаксис сценария sed:

[address]command[options]
  • Адрес: может быть номером строки, диапазоном номеров строк или регулярным выражением.
  • Команда: всего одна буква, например s, что означает "заменить".
  • Опции: зависит от команды

«Хотел бы я знать это намного раньше.

Обратите внимание, что в sed не так много пробелов между этими элементами. В macOS (BSD) sed пробел между адресом и командой допустим.

Давайте посмотрим, как это работает, на нескольких примерах.

Заменитель, s

sed 's/target/replacement/g' file.txt

Это глобально заменит слово target на replacement. Адрес не указан, поэтому он применяется во всем мире.

Это команда s, что означает «заменить». Для параметров s принимает регулярное выражение, равное /target/replacement/g. Флаг g - это глобальный флаг регулярного выражения. Это гарантирует замену каждого совпадения.

Итак, если мы хотим предоставить адрес, чтобы ограничить, к каким строкам применяется команда, мы могли бы сделать что-то вроде этого:

sed '1,5 s/target/replacement/g' file.txt

То же, что и раньше, но только строки с 1 по 5 будут затронуты командой.

Очень часто подстановка используется для удаления чего-либо в строке, например:

sed 's/remove//g' file.txt

Это удалит все вхождения слова «удалить», просто заменив его ничем.

Удалить, d

Команда удаления удалит всю строку. Допустим, мы хотели использовать регулярное выражение, чтобы определить, какие строки мы хотим удалить. Мы передаем это регулярное выражение в address, например:

sed '/^[[:space:]]/ d' file.txt

Это удалит (d в sed) все строки, начинающиеся с пробела, новой строки или табуляции. d не имеет вариантов. (Примечание [[:space:]] - это класс символов регулярное выражение bash. Между регулярным выражением JavaScript и регулярным выражением bash есть некоторые различия.)

Изменить на месте, -i

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

sed '/^[[:space:]]/ d' file.txt > file-modified.txt

Но что, если мы захотим изменить его на месте?

sed -i '/^[[:space:]]/ d' file.txt

Флаг -i, который означает «на месте», сделает именно это. Но вот предостережение - хотя это работает так же, как и в Linux / GNU sed (который может быть установлен в macOS, если вы того пожелаете), по умолчанию в macOS sed это не работает. Это потому, что вы можете передать аргумент флагу -i, который необязателен в новых версиях sed, но необходим в старых. Этот аргумент указывает расширение файла резервной копии.

sed -i '.bak' '/^[[:space:]]/ d' file.txt

Эта команда создает файл file.txt.bak из исходного источника и устанавливает измененный текст в файл file.txt. Вы можете передать пустую строку в -i, чтобы он не создавал резервную копию. Это то, что я делаю в старом sed macOS, когда хочу редактировать на месте без резервной копии.

sed -i '' '/^[[:space:]]/ d' file.txt

Вставить, я

Допустим, у меня есть куча файлов, в которые я хочу вставить общий заголовок. Поскольку GNU sed новее и намного проще, чем macOS, мы начнем с этого:

sed -i '1 i // Copyright 2019 Cameron Nokes' *.js

Таким образом, мы передаем флаг -i в sed без аргументов, указывающих на необходимость изменения на месте. Затем в нашем сценарии sed мы хотим вставить в первую строку, поэтому address будет 1. Затем i, для вставки. Затем идет контент, который мы хотим вставить. После сценария sed мы передаем глобус *.js, чтобы сообщить ему об изменении всех файлов .js в этом каталоге. Это довольно просто.

В macOS все сложнее, и я лично изо всех сил пытался сделать это только в командной строке, поэтому я создал сценарий:

for file in $(ls *.js); do
  sed -i '' '1 i\
  // Copyright 2019 Cameron Nokes
  ' $file
done

Здесь следует указать на несколько важных отличий:

  1. macOS sed не может работать с несколькими файлами, поэтому я просматриваю их в цикле
  2. Команда sed i в macOS должна сопровождаться \ и новой строкой.

К сожалению, в macOS синтаксис становится немного неудобным.

Несколько команд

Вы можете выполнять несколько команд sed в одном скрипте, разделяя их точкой с запятой.

sed '1 d; 2,5 s/target/replacement/g' file.txt

Это удалит первую строку, а затем произведет замену в строках со 2 по 5.

Это все для sed. Sed может даже немного больше! Вы можете прочитать полное руководство здесь https://www.gnu.org/software/sed/manual/sed.html.

Первоначально опубликовано на https://cameronnokes.com 19 декабря 2019 г.