Как найти слово, хранящееся в области удержания, с помощью sed?

Существует две опции. Можно использовать -- сказать rm то, что больше нет опций. Второй путь состоит в том, чтобы указать относительный или полный путь в файл.

Вот некоторые примеры:

rm -- -f
rm ./-f
rm /path/to/-f
3
28.10.2015, 11:48
2 ответа

Это было сложно. Предположим, что у вас есть файл следующего вида:

$ cat file
word
line with a word and words and wording wordy words.

Где:

  • Строка 1: это шаблон поиска, который должен быть помещен в пространство удержания и процитирован в `word`.
  • Строка 2: это строка для глобального поиска и замены.

Команда sed:

sed -n '1h; 2{x;G;:l;s/^\([^\n]\+\)\n\(.*[^`]\)\1\([^`]\)/\1\n\2`\1`\3/;tl;p}' file

Explanation:

  • 1h; сохраняет первую строку в hold space (это ожидание, которое мы хотим искать).
    • hold space contains: word
  • 2{...} применяется ко второй строке.
  • x; обменивает пространство шаблона и пространство удержания.
  • G; добавляем пространство удержания к пространству шаблона. Теперь в пространстве шаблонов мы имеем:
word # I will call this line the "pattern line" from now on
line with a word and words and wording wordy words.
  • :l; устанавливаем метку l как точку для последующего использования.
  • s/// выполняем фактический поиск/замену в пространстве шаблонов, упомянутом выше:
    • ^\([^\n]\+\)\n поиск в "строке шаблона" всех символов (с начала строки ^), которые не являются новой строкой [^\n] (один или несколько раз \+), до новой строки \n. Теперь она сохраняется в обратной ссылке \1. Она содержит "шаблонную строку".
    • (.*[^`]) поиск любого символа .*, за которым следует символ, не являющийся обратным знаком [^`]. Это хранится в \2. \2 теперь содержит: строка со словом и слова и формулировки словесные, до последнего появления слова, потому что...
    • \1 является следующим поисковым термином (обратная ссылка \1, слово), отсюда следует, что "шаблонная строка" содержит.
    • ([^`]) за этим следует другой символ, который не является обратным знаком; сохраняется для ссылки \3. Если мы не сделаем этого (и часть в \2 сверху), мы окажемся в бесконечном цикле, цитируя одно и то же слово, снова и снова -> ````word````, потому что s/// всегда будет успешным и tl; перепрыгнет обратно к : l (см. tl; далее).
    • \1\n\2\1\3 все вышеперечисленное заменяется обратными ссылками. Второе \1 - это то, что мы должны процитировать (обратите внимание, что первая ссылка - это "шаблонная строка").
  • tl; если s/// был успешным (мы что-то заменили), переходим к метке l и начинаем снова, пока больше нечего искать и заменять. Это происходит, когда заменяются/цитируются все вхождения слова.
  • p; когда все будет сделано, выведите измененную строку (пробел в шаблоне).

The output:

$ sed -n '1h; 2{x;G;:l;s/^\([^\n]\+\)\n\(.*[^`]\)\1\([^`]\)/\1\n\2`\1`\3/;tl;p}' file
word
line with a `word` and `word`s and `word`ing `word`y `word`s.
4
27.01.2020, 21:12

Таблицы поиска могут быть трудными - и дорогими - потому что вам придется искать на обоих концах пространства шаблонов одновременно. Хотя, по крайней мере, это можно реализовать более или менее просто. Вы должны учитывать, что независимо от того, что вы делаете, вы можете надежно обрабатывать только одно совпадение за раз, и поэтому вы можете отказаться от всякой надежды на общий результат g . В любом случае это только запутает - вы не работаете с скомпилированным выражением, вы буквально работаете с побочными эффектами и обеими сторонами для загрузки.

printf  %s\\n some words to match \
        'and some words and some more words to match them against' |
sed  -ne'$!{H;d;}' -e'G;s/\(\n\).*/\1&\1/;tm' -e:m \
     -e 's/\(.\)\(.*\)\(.*\n\n.*\n\1\2\(\n\)\)/`\1\4\2`\3/;tm'

Это основной цикл. На самом деле он еще не работает, потому что я еще не убираю его там, но он решает основную проблему.Поскольку вам нужно многократно перебирать одно и то же пространство шаблонов, как вы можете быть уверены, что совпадение не совпадает дважды, верно? Если вы закроете его с каким-либо разделителем, вы все равно снова сопоставите, и вы просто будете складывать подставки для книг до бесконечности.

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

Вот еще кое-что:

printf  %s\\n some words to match \
        'and some words and some more words to match them against' |
sed  -ne'$!{H;d;}' -e'G;s/\(\n\).*/\1&\1/;tm' -e:m \
     -e 's/\(.\)\(.*\)\(.*\n\n.*\n\1\2\(\n\)\)/`\1\4\2`\3/;tm' \
     -e  l

and `s\nome` `w\nords` and `s\nome` more `w\nords` `t\no` `m\natch` \
them against\n\n\nsome\nwords\nto\nmatch\n$

И очистка, конечно, проста:

printf  %s\\n some words to match \
        'and some words and some more words to match them against' |
sed  -ne'$!{H;d;}' -e'G;s/\(\n\).*/\1&\1/;tm' -e:m \
     -e 's/\(.\)\(.*\)\(.*\n\n.*\n\1\2\(\n\)\)/`\1\4\2`\3/;tm' \
     -e 's/\(`.\)\n/\1/g;P'

and `some` `words` and `some` more `words` `to` `match` them against

По крайней мере, вы можете сделать g локально.


Я предпочитаю делать подобные вещи, создавая для этого сценарий.

printf  %s\\n some words to match \
        'and some words and some more words to match them against' |
{   sed -e"$(
        sed -ne'$w /dev/fd/3' -e$\q     \
             -e 's/[]\^$/.*[]/\\&/g'    \
             -e 's|..*|s/&/`\&`/g|p'
    )"  <&3
}   3<<""    3<>/dev/fd/3

and `some` `words` and `some` more `words` `to` `match` them against

sed в подстановке команд записывает оператор подстановки sed s /// ubstitution после того, как позаботится об экранировании любых метасимволов в любой строке ввода, кроме последней, которая может содержать. Последняя строка w буквально обращается к совместно используемому файловому дескриптору here-doc для внешнего sed для чтения в качестве входных данных. Внутренний sed печатает сценарий, который работает как:

sed -e's/some/`&`/g'  \
    -e's/words/`&`/g' \
    -e's/to/`&`/g'    \
    -e's/match/`&`/g'

... и передает последнюю строку другому sed для последующей обработки.

3
27.01.2020, 21:12

Теги

Похожие вопросы