Я учусь Сед. Казалось, что все идет хорошо, пока я не наткнулся на N (многострочный следующий). Я создал этот файл (guide.txt) для практики / понимания / контекста. Вот содержимое указанного файла ...
This guide is meant to walk you through a day as a Network
Administrator. By the end, hopefully you will be better
equipped to perform your duties as a Network Administrator
and maybe even enjoy being a Network Administrator that much more.
Network Administrator
Network Administrator
I'm a Network Administrator
Поэтому моя цель - заменить ВСЕ экземпляры «Администратор сети» на «Системный пользователь». Поскольку первый экземпляр «Администратора сети» отделен новой строкой (\ n), мне нужен многострочный оператор следующего оператора (N), чтобы добавить строку, начинающуюся с «Администратор», с предыдущей строкой, заканчивающейся «Сеть \ n» , Нет проблем. Но я также хочу перехватить все другие однострочные экземпляры «Администратор сети».
Из моего исследования я узнал, что мне понадобятся две команды замещения; один для новой строки, разделенной строкой, и один для остальных. Кроме того, произошел некоторый джайв из-за последней строки, содержащей совпадение подстановки и следующей многострочной. Так что я создаю это ...
$ sed '
> s/Network Administrator/System User/
> N
> s/Network\nAdministrator/System\nUser/
> ' guide.txt
Это возвращает эти результаты ...
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a Network Administrator that much more.
System User
Network Administrator
I'm a System User
Я думал, что однострочная подстановка перехватит все «нормальные» экземпляры «Network Administrator» и поменяет их на «System User», тогда как многострочный оператор сработает с магией на экземпляре, отделенном символом новой строки, но, как вы могу видеть, что это вернуло, что я считаю, неожиданные результаты.
После некоторой возни я приземлился на это ...
$ sed '
> s/Network Administrator/System User/
> N
> s/Network\nAdministrator/System\nUser/
> s/Network Administrator/System User/
> ' guide.txt
И вуаля, я получаю желаемый результат ...
This guide is meant to walk you through a day as a System
User. By the end, hopefully you will be better
equipped to perform your duties as a System User
and maybe even enjoy being a System User that much more.
System User
System User
I'm a System User
Почему это работает, а оригинальный скрипт sed - нет? Я очень хочу это понять.
Заранее благодарю за любую помощь.
Ответы:
Пока вы
sed
учитесь, я потрачу время, чтобы добавить ответ @ John1024:1) Обратите внимание, что вы используете
\n
в строке замены. Это работает в GNUsed
, но не является частью POSIX, поэтому он вставит обратную косую черту иn
во многие другиеsed
(используя\n
в шаблоне переносимый, кстати).Вместо этого я предлагаю сделать
s/Network\([[:space:]]\)Administrator/System\1User/g
: The[[:space:]]
будет соответствовать новой строке или пробелу, поэтому вам не нужно двеs
команды, но объедините их в одну. Окружив его,\(...\)
вы можете ссылаться на него в замене: он\1
будет заменен тем, что было найдено в первой паре\(\)
.2) Чтобы правильно сопоставить шаблоны по двум строкам, вы должны знать
N;P;D
шаблон:N
Всегда добавьте следующую строку (для последней строки , за исключением, поэтому это «имя» с$!
(= если не последняя строка, вы всегда должны рассмотреть , чтобы предшествоватьN
с ,$!
чтобы избежать случайного окончания сценария) Затем , после замены в.P
Только печатает первая строка в пространстве шаблона иD
удаляет эту строку и начинает следующий цикл с остатками пространства шаблона (без чтения следующей строки). Это, вероятно, то, что вы изначально хотели.Запомните этот шаблон, он вам часто понадобится.
3) Еще один полезный шаблон для многострочного редактирования, особенно когда задействовано более двух строк: задержать сбор проб, как я предложил Джону:
Я повторяю это, чтобы объяснить это:
H
добавляет каждую строку к пробелу. Так как это приведет к дополнительному переводу строки перед первой строкой, необходимо добавить первую строку вместо добавления1h
. Следующее$!d
означает «для всех строк, кроме последней, удалите пробел и начните сначала». Таким образом, остальная часть сценария выполняется только для последней строки. На этом этапе весь файл собирается в удерживающем пространстве (поэтому не используйте его для очень больших файлов!) Иg
перемещает его в пространство образца, так что вы можете выполнять все замены сразу, как вы можете с-z
опцией GNUsed
.Это еще одна полезная модель, которую я предлагаю иметь в виду.
источник
Во-первых, обратите внимание, что ваше решение на самом деле не работает. Рассмотрим этот тестовый файл:
И затем выполните команду:
Проблема в том, что код не заменяет последний
Network\nAdministrator
.Это решение работает:
Мы также можем применить это к вашему
guide.txt
:Ключ заключается в том, чтобы продолжать читать в строках, пока не найдете тот, который не заканчивается
Network
. Когда это будет сделано, замены могут быть сделаны.Примечание о совместимости: все вышеперечисленное используется
\n
в тексте замены. Это требует GNU sed. Это не будет работать на седе BSD / OSX.[Шляпа на Филиппосе .]
Многострочная версия
Если это поможет уточнить, вот та же команда, разделенная на несколько строк:
Как это работает
:a
Это создает ярлык
a
./Network$/{ $!{N;ba} }
Если эта строка заканчивается на
Network
, то, если это не последняя строка ($!
), прочитайте и добавьте следующую строку (N
) и вернитесь к labela
(ba
).s/Network\nAdministrator/System\nUser/g
Сделайте замену с промежуточным переводом строки.
s/Network Administrator/System User/g
Сделайте замену с промежуточным пробелом.
Более простое решение (только GNU)
С GNU sed ( не BSD / OSX) нам нужна только одна команда замены:
И в
guide.txt
файле:В этом случае
-z
говорит sed читать до первого NUL-символа. Поскольку текстовые файлы никогда не имеют нулевого символа, это приводит к чтению всего файла за один раз. Затем мы можем сделать замену, не беспокоясь о пропущенной строке.Этот метод не подходит, если файл огромен (обычно это гигабайты). Если он такой большой, то одновременное чтение всего этого может привести к нагрузке на системную память.
Решение, которое работает как на GNU, так и на BSD sed
Как предположил Филлипос , следующее решение является переносимым:
источник
Network Administrator
разделить между первой и второй строкой этой пары, ваше решение успешно выполнит замену. Затем он печатает эти две строки и читает следующую пару. Однако, если вторая строка первой пары заканчивается,Network
а первая строка второй пары начинается сAdministrator
, код пропускает ее. Мой код избегает этого, читая в строках, пока не найдет тот, который не заканчиваетсяNetwork
.sed
:\n
в замене не определено в стандарте.sed 'H;1h;$!d;x;s/Network\([[:space:]]\)Administrator/System\1User/g'
это портативный способ сделать это.