Какой смысл использовать несколько восклицательных знаков в sed?


12

В документации POSIX sed сказано:

Перед функцией может стоять один или несколько символов '!' символы, в этом случае функция должна применяться, если адреса не выбирают пространство шаблона. Ноль или более <пробел> символов должны быть приняты до первого '!' персонаж. Не указано, могут ли символы <blank> следовать за '!' характер и соответствующие приложения не должны следовать за «!» символ с <пробелом> символов.

Итак, с любым POSIX sed мы можем:

sed -e '/pattern/!d' file

Это так же, как писать:

sed -e '/pattern/!!d' file

А !!!dи nвосклицательных знаков все еще быть штраф (протестировано с тремя sedверсии от реликвия Toolchest ). Я не вижу никакой выгоды между несколькими, а не одним восклицательным знаком.

Почему спецификация допускает такой синтаксис и как он полезен в реальных приложениях?


Похоже, что GNU sed в этом случае не соответствует, и будет жаловаться, если мы будем использовать несколько восклицаний:

$ sed -e '/pattern/!!d' file
sed: -e expression #1, char 11: multiple `!'s

2
FWIW: На OpenBSD !действует как переключатель, /pattern/!!такой же , как /pattern/и /pattern/!!!тот же /pattern/!. На FreeBSD несколько !одинаковы.
lcd047

2
Дело в том, что в спецификации sedмогут быть сгенерированы скрипты . Учитывая POSIX sed, это должно быть действительно простым делом, чтобы написать сценарий написания sedсценария. И поэтому, если у вас есть какой-то триггер для какого-то случая, который должен пометить адрес, !не достойный того, каким было ваше действие, вы могли бы даже запустить его несколько раз для одного и того же, и при этом получить те же результаты.
mikeserv

@cuonglm Нет, только FreeBSD есть. GNU, OpenBSD и NetBSD sed- нет.
lcd047

@ lcd047: да, конечно. Извините за мой плохой английский. Я имею в виду, что это не соответствует, не так ли? Приятно это знать. Но главное в моем вопросе - как этот синтаксис может быть полезен в реальном мире с POSIX sed?
cuonglm

1
FWIW: исправление было зафиксировано в OpenBSD-current.
lcd047

Ответы:


5

sedAPI примитивен - и это по замыслу. По крайней мере, он оставался примитивным по замыслу - не мог сказать, был ли он примитивно разработан в начале. В большинстве случаев написание sedсценария, который при запуске выдаст другой sedсценарий , действительно прост. sedочень часто применяется таким образом макропроцессорами, такими как m4и / или make.

(Ниже приводится весьма гипотетический вариант использования: это проблема, разработанная для решения проблемы. Если вам кажется, что это растягивается, то, вероятно, это так, но это не обязательно делает ее менее обоснованной.)


Рассмотрим следующий входной файл:

cat <<"" >./infile
camel
cat dog camel
dog cat
switch
upper
lower

Если бы мы хотели написать sedскрипт, который добавлял бы слово- case к хвосту каждого подходящего слова в указанном выше входном файле, только если его можно было найти в строке в соответствующем контексте , и мы хотели бы сделать это максимально эффективно ( как должно быть нашей целью, например, во время операции компиляции), тогда мы должны предпочесть избегать применения /регулярных выражений /в максимально возможной степени.

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

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

Например:

n=$(printf '\\\n\t')
grep -En 'camel|upper|lower' <infile |
sed "   1i${n%?}#!/usr/heirloom/bin/posix2001/sed -nf
        s/[^:]*/:&$n&!n;&!b&$n&/;s/://2;\$a${n%?}q"'
        s/ *cat/!/g;s/ *dog/!/g
        s| *\([cul][^ ]*\).*|s/.*/\1-case/p|'

... который записывает вывод в виде sedскрипта и который выглядит как ...

#!/usr/heirloom/bin/posix2001/sed -nf
:1
    1!n;1!b1
    1s/.*/camel-case/p
:2
    2!n;2!b2
    2!!s/.*/camel-case/p
:5
    5!n;5!b5
    5s/.*/upper-case/p
:6
    6!n;6!b6
    6s/.*/lower-case/p
q

Когда этот вывод сохраняется в исполняемом текстовом файле на моем компьютере с именем ./bang.sedи запускается как ./bang.sed ./infile, вывод:

camel-case
upper-case
lower-case

Теперь вы можете спросить меня ... Зачем мне это делать? Почему бы мне не просто grepспички якоря ? Кто использует верблюжий чемодан? И на каждый вопрос, который я мог только ответить, я понятия не имею ... потому что я не знаю. До прочтения этого вопроса я никогда лично не замечал мульти! требование разбора в спецификации - я думаю, что это довольно аккуратный улов.

Мульти-! вещь сделал сразу имеет смысл для меня, хотя - большая часть sedспецификации ориентирована просто разобраны и просто сгенерированных sed сценариев. Вы, вероятно, найдете требуемые \nразделители ewline для [wr:bt{]большего смысла в этом контексте, и если вы будете помнить об этой идее, вы могли бы лучше понять некоторые другие аспекты спецификации - (например, :не принимать адреса и qотказываться от принять больше, чем 1) .

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

Я считаю, что мульти! адреса могут быть более полезными в этом контексте, чем в некоторых других, но, честно говоря, я не могу вспомнить ни одного случая, в котором я мог бы использовать его очень хорошо - и мне sedэто очень нравится. Я также думаю, что стоит отметить, что GNU / BSD sedоба не справляются с этим так, как указано - это, вероятно, не тот аспект спецификации, который пользуется большим спросом, и поэтому, если реализация игнорирует его, я очень серьезно сомневаюсь, что их баги @ box будут страдать. ужасно в результате.

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


1
Это сейчас исправлено в OpenBSD-current.
lcd047

1
Множественный !будет удален в следующей спецификации , что здесь происходит!
Cuonglm

@cuonglm - я думаю, слишком поздно. возможно я был ближе к отметке, чем я думал.
mikeserv

@cuonglm - хорошо, хорошо, но что это значит ... Принятый как помеченный вообще означает?
mikeserv

1
@mikeserv: ответ объяснил мое удивление и дал мне другое представление о sed API. Это имеет смысл для меня!
cuonglm
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.