Как заменить все пробелы в квадратных скобках подчеркиванием с помощью sed? - PullRequest
2 голосов
/ 21 декабря 2010

Я понял, что для того, чтобы превратить [какое-то имя] в [какое-то_имя], мне нужно использовать следующее выражение:

s/\(\[[^ ]*\) /\1_/

, то есть создать захват обратной ссылки для всего, что начинается с литерала '[', который содержит любое количество непробельных символов, за которыми следует пробел, которые должны быть заменены непробельными символами, за которыми следует подчеркивание.Однако пока я не знаю, как изменить это выражение, чтобы оно работало для ВСЕХ подчеркиваний в фигурных скобках, например [несколько слов] в [a_few_words].

Я чувствую, что я близок, но япропущен кусок знаний, который откроет ключ к тому, чтобы эта штука работала бесконечное число раз в пределах ограничений первого набора [], содержащихся в строке (в данном случае SQL Server DDL).

Любые предложения с благодарностью получены ....

Ответы [ 2 ]

3 голосов
/ 22 декабря 2010

Для обмана необходимы две части:

  1. Прекратите замену, когда вы достигнете закрывающей квадратной скобки (но сделайте это несколько раз в строке):

    s/\(\[[^] ]*\) /\1_/g
    

    Это соответствует открытой квадратной скобке, за которой следует ноль или более символов, которые не являются ни пробелом, ни закрывающей квадратной скобкой. Глобальный суффикс означает, что шаблон применяется ко всем последовательностям, начинающимся с открытой квадратной скобки, за которой в конце концов следует пустая или закрытая квадратная скобка в строке. Также обратите внимание, что это регулярное выражение не меняет '[single-word] and context', тогда как оригинал переведет его в '[single-word]_and context', который не является объектом упражнения.

  2. Получите sed, чтобы повторить поиск с того места, где этот начался. К сожалению, нет действительно хорошего способа сделать это. Sed всегда возобновляет поиск после замещенного текста; и это один раз, когда мы этого не хотим. Иногда вы можете просто повторить операцию замены. В этом случае вы должны повторять это каждый раз, когда замена завершается успешно, останавливаясь, когда нет больше замен.

Двумя менее известными операциями в sed являются команды :label и t. Они присутствовали в 7-м издании Unix (около 1978 г.), поэтому они не являются новыми функциями. Первый просто определяет позицию в скрипте, к которой можно перейти с помощью 'b' (здесь не требуется) или 't':

[2addr]t [label]

Переход к функции ':' с меткой, если были произведены какие-либо замены после самого последнего чтения строки ввода или выполнения функции t. Если метка не указана, переходите к концу скрипта.

Изумительно: нам нужно:

 sed -e ':redo; s/\(\[[^] ]*\) /\1_/g; t redo' data.file

За исключением - он не работает все в одной строке, как эта (по крайней мере, не в MacOS X). Это сработало превосходно, хотя:

sed -e ':redo
        s/\(\[[^] ]*\) /\1_/g
        t redo' data.file

Или, как отмечено в комментариях, вы можете написать три отдельных параметра '-e' (которые работают в MacOS X):

 sed -e ':redo' -e 's/\(\[[^] ]*\) /\1_/g' -e 't redo' data.file

С учетом файла данных:

a line with [one blank] word inside square brackets.
a line with [two blank] or [three blank] words inside square brackets.
a line with [no-blank] word inside square brackets.
a line with [multiple words in a single bracket] inside square brackets.
a line with [multiple words in a single bracket] [several times on one line]

вывод из показанного сценария sed:

a line with [one_blank] word inside square brackets.
a line with [two_blank] or [three_blank] words inside square brackets.
a line with [no-blank] word inside square brackets.
a line with [multiple_words_in_a_single_bracket] inside square brackets.
a line with [multiple_words_in_a_single_bracket] [several_times_on_one_line]

И, наконец, читая мелкий шрифт в вопросе, если вам нужно, чтобы это было сделано только в первом поле в квадратных скобках в каждой строке, тогда мы должны убедиться, что нет открытых квадратных скобок перед тем, который начинает соответствие , Этот вариант работает:

sed -e ':redo' -e 's/^\([^]]*\[[^] ]*\) /\1_/' -e 't redo' data.file

(Квалификатор 'g' пропал - он, вероятно, не нужен в других вариантах также с учетом цикла; его присутствие может сделать процесс незначительно более эффективным, но, скорее всего, это будет практически невозможно обнаружить. шаблон теперь привязан к началу строки (каретки) и содержит ноль или более символов, которые не являются квадратными скобками перед первой открытой квадратной скобкой.)

Пример вывода:

a line with [two_blank] or [three blank] words inside square brackets.
a line with [no-blank] word inside square brackets.
a line with [multiple_words_in_a_single_bracket] inside square brackets.
a line with [multiple_words_in_a_single_bracket] [several times on one line]
1 голос
/ 22 декабря 2010

Это проще в языке, подобном perl, в котором есть «исполняемые» замены:

perl -wne 's/(\[.*?])/ do { my $x = $1; $x =~ y, ,_,; $x } /ge; print'

Или разделить его более четко:

sub replace_with_underscores {
    my $s = shift;
    $s =~ y/ /_/;
    $s
}
s/(\[.*?])/ replace_with_underscores($1) /ge;

.*? - это не жадное совпадение (во избежание смешивания двух смежных фраз в скобках), а флаг e для замены приводит к его оценке, поэтому вы можете вызвать функцию для выполнения внутренней работы.

...