sed заменяет пробелы на тире только между определенными шаблонами символов

У меня есть такие строки:

ОРИГИНАЛ

sometext1 sometext2 word:A12 B34 C56 sometext3 sometext4
sometext5 sometext6 word:A123 B45 C67 sometext7 sometext8
sometext9 sometext10 anotherword:(someword1 someword2 someword3) sometext11 sometext12

ОТРЕДАКТИРОВАНО

asdjfkklj lkdsjfic kdiw:A12 B34 C56 lksjdfioe sldkjflkjd
lknal niewoc kdiw:A123 B45 C678 oknes lkwid 
cnqule nkdal anotherword:(kdlklks inlqok mncvmnx) unqieo lksdnf

Желаемый результат:

asdjfkklj lkdsjfic kdiw:A12-B34-C56 lksjdfioe sldkjflkjd
lknal niewoc kdiw:A123-B45-C678 oknes lkwid 
cnqule nkdal anotherword:(kdlklks-inlqok-mncvmnx) unqieo lksdnf

РЕДАКТИРОВАНО: Будет ли это более явным? Но, честно говоря, это гораздо сложнее читать и отвечать, чем писать sometext#. Я не знаю предпочтения людей.

Я хочу заменить пробелы тире только после буквы алфавита, за которой следуют некоторые цифры, И заменить пробелы тире между словами между двумя скобками. И никаких других пробелов в строке. Также был бы признателен за объяснение синтаксиса.

Спасибо!


person Char    schedule 26.10.2017    source источник
comment
Почему между t2 w в sometext2 word нет пробела, который соответствует критериям буквы, за которой следует цифра. Должно ли быть несколько цифр. Должен ли это быть один символ, ограниченный границей?   -  person 123    schedule 26.10.2017
comment
Всегда ли три части должны быть соединены тире?   -  person Armali    schedule 26.10.2017
comment
@123 sometext1 sometext2 просто означает набор текста. Я просто использую числа, чтобы показать, что они содержат разные символы. То же самое для другой группы слов в третьем примере.   -  person Char    schedule 26.10.2017
comment
@ Armali Нет, групп может быть больше 3. То же самое для групп слов в третьем примере.   -  person Char    schedule 26.10.2017


Ответы (3)


Это может сработать для вас (GNU sed):

sed -r ':a;s/(A[0-9]+(-[A-Z][0-9]+)*) ([A-Z][0-9]+)/\1-\3/;ta;s/(\(\S+(-\S+)*) (\S+( \S+)*\))/\1-\3/;ta' file

Итеративно заменяйте пробелы в необходимых строках, используя регулярное выражение и обратные ссылки.

person potong    schedule 26.10.2017

Этот код работает хорошо

darby@Debian:~/Scrivania$ cat test.txt | sed -r 's@\s+([A-Z][0-9]+)@-\1@g' | sed ':l s/\(([^ )]*\)[ ]/\1-/;tl'
asdjfkklj lkdsjfic kdiw:A12-B34-C56 lksjdfioe sldkjflkjd
lknal niewoc kdiw:A123-B45-C678 oknes lkwid 
cnqule nkdal anotherword:(kdlklks-inlqok-mncvmnx) unqieo lksdnf

Объясните мое регулярное выражение

В первом регулярном выражении

Options

-r              Enable regex extended

Pattern

\s+             One or more space characters
([A-Z][0-9]+)   Submatch a uppercase letter and one or more digits

Replace

-              Dash character
\1             Previous submatch

Note

The g after delimiters ///g is for global substitution.

Во втором регулярном выражении

Pattern

:l             label branched to by t or b
tl             jump to label if any substitution has been made on the pattern space since the most recent reading of input line or execution of command 't'. If label is not specified, then jump to the end of the script. This is a conditional branch
\(([^ )]*\)    match all in round brackets and stop to first space found
[ ]            one space character

Replace

\1             Previous submatch
-              Add a dash
person Darby_Crash    schedule 26.10.2017
comment
Не работает с sometext5 sometext6 word:A123 B45 C678 D888 sometext7 sometext8 или sometext5 sometext6 word:A123 B45 sometext7 sometext8 - person Indent; 26.10.2017
comment
Хотя этот фрагмент кода может решить вопрос, включение объяснения действительно помогает улучшить качество вашего поста. Помните, что вы отвечаете на вопрос для будущих читателей, и эти люди могут не знать причин вашего предложения кода. - person Dr Rob Lang; 26.10.2017
comment
Таким образом, кошка не нужна: sed 's/ ([A-Z])/-\1/g;:l s/(([^ )]*) /\1-/;tl' test.txt - person ctac_; 26.10.2017
comment
Теперь я понимаю, что добавление цифры после некоторого текста означает для некоторых людей буквально наличие числа после некоторых символов. А некоторые люди воспринимают какой-то текст буквально как слово с символами s, o, m, e, t, e, x, t. Прошу прощения за путаницу. Простите мой новичок в регулярных выражениях. Мой контекст здесь таков, что sometext# представляет собой строку символов, которая может или не может образовывать удобочитаемое слово, и, скорее всего, будет полностью отличаться от другого sometext#, либо с другими символами, либо с теми же символами в разных комбинациях, и может быть разная длина. Я отредактирую свой вопрос. - person Char; 26.10.2017
comment
Теперь я объяснил свой код. Я надеюсь, что это может помочь вам. - person Darby_Crash; 26.10.2017

Вам нужно захватить первую буквенно-цифровую группу, используя () и вторую группу. Затем вы можете просто заменить все, используя обратные ссылки \1 и \2 :

используя sed дважды

sed -E 's/(\b[A-Za-z][0-9]+) ([A-Z])/\1-\2/g' | sed -E 's/(\b[A-Za-z][0-9]+) ([A-Z])/\1-\2/g' 

или используя perl (с lookahead (?=...)регулярное выражение не захватывает 2-ю группу)

perl -pe 's/(\b[A-Za-z][0-9]+) (?=[A-Z])/\1-/g'


\b рабочая граница
[A-Za-z] 1 буква
[0-9]+ 1 или более цифр

sed не поддерживает функции lookahead и lookbehind

person Indent    schedule 26.10.2017
comment
Спасибо. Но он заменил только первый пробел на тире. sometext1 word:A12-B34 C56 sometext2. Не могли бы вы уточнить выражение, пожалуйста? - person Char; 26.10.2017
comment
Если я выполняю его во второй раз, то он работает, заменяя второй пробел word:A12-B34-C56. Но второй раз запускать не надо? Можно ли произвести замену в одном исполнении? - person Char; 26.10.2017