Який сенс використовувати кілька знаків оклику в 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.
lcd047

Відповіді:


5

sedAPI API примітивний - і це задумом. Принаймні, він залишився примітивним по дизайну - чи був він створений примітивно на початку, я не можу сказати. У більшості випадків написання sedсценарію, який при запуску видасть інший sedсценарій , справді є простою справою. sedдуже часто застосовується таким чином макропроцесорами, такими як m4та / або make.

(Далі - це дуже гіпотетичний випадок використання: це проблема, розроблена для рішення рішення. Якщо вам це здається розтягнутим, то це, мабуть, тому, що воно є, але це не обов'язково робить його менш вагомим.)


Розглянемо наступний вхідний файл:

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

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

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

Ще одна річ, яку ми можемо зробити, це обробити файл тепер проти regexps. Ми можемо створити - і включити до нашої компіляції - 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дуже багато. Я також вважаю, що примітним є те, sedщо обидва GNU / BSD не справляються з цим, як зазначено - це, мабуть, не аспект специфікації, який користується великим попитом, і тому, якщо реалізація проглядає це, я сумніваюся дуже серйозно, їх помилки @ box постраждають в результаті жахливо.

Однак, невдача з цим завданням є помилкою для будь-якої реалізації, яка претендує на відповідність, і тому я думаю, що для цього потрібно запустити електронний лист у відповідні поля розробників, і я маю намір це зробити, якщо ви цього не зробите.


1
Тепер він зафіксований у OpenBSD-струмі.
lcd047

1
Кілька !буде видалено в наступній специфікації , що тут відбувається!
cuonglm

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

@cuonglm - ну гаразд, але що це ... Прийняте як Позначене навіть означає?
mikeserv

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