Как заменить все пробелы в квадратных скобках подчеркиванием с помощью SED?

StackOverflow https://stackoverflow.com/questions/4503535

  •  12-10-2019
  •  | 
  •  

Вопрос

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

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

т.е. создать захват обратной ссылки для всего, что начинается с буквального «[», которое содержит любое количество не пространственных символов, за которым следует пространство, которое будет заменено не пространственными символами, за которым следует подчеркивание. Чего я еще не знаю, так это как изменить это выражение, поэтому оно работает для всех подчеркиваний в скобках, например, [несколько слов] в [a_few_words].

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

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

Это было полезно?

Решение

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

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

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

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

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

Две из менее известных операций в sed есть ':labelt'Команды. Они присутствовали в 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]

Другие советы

Это проще на таком языке, как 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 Флаг на замену приводит к оценке его, поэтому вы можете назвать функцию для выполнения внутренней работы.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top