Замена четных или нечетных совпадений строк - PullRequest
0 голосов
/ 14 января 2019

Сразу же: речь идет не только о совпадении чисел.

В документе имеется множество **, разбросанных вокруг , и мне нужно заменить их на 1 и 2.

Пример ввода

**Lorem ipsum dolor **sit amet, consectetur adipisicing elit. ** 
Ad velit delectus ** quidem itaque eum **accusamus reprehenderit**
illo culpa **** praesentium** ea fugit ****hic in vel officiis, 
expedita sit **** et harum enim quaerat, **** ab corporis quo 
atque perspiciatis. Minima odit obcaecati** ** reiciendis, sed 
rerum ** labore. In fuga, ** aspernatur earum aliquid ** ****** 
**commodi delectus?

Желаемый вывод

1Lorem ipsum dolor 2sit amet, consectetur adipisicing elit. 1 
Ad velit delectus 2 quidem itaque eum 1accusamus reprehenderit2
illo culpa 12 praesentium1 ea fugit 21hic in vel officiis, 
expedita sit 21 et harum enim quaerat, 21 ab corporis quo 
atque perspiciatis. Minima odit obcaecati2 1 reiciendis, sed 
rerum 2 labore. In fuga, 1 aspernatur earum aliquid 2 121 
2commodi delectus?

Единственное, что я имею в виду, это сделать цикл, который заменяет первое найденное вхождение (s/\*{2}/1/), затем второе (s/\*{2}/2/), промыть и повторить, но так как я все еще не знаю, как перевести это в шеллскрипт, мне было интересно, выполнимо ли это с помощью пары команд sed.

Ответы [ 4 ]

0 голосов
/ 14 января 2019

С GNU awk для мульти-символьных RS и RT:

$ awk -v RS='[*][*]' '{ORS=(RT ? (ORS%2+1) : "")}1' file
1Lorem ipsum dolor 2sit amet, consectetur adipisicing elit. 1
Ad velit delectus 2 quidem itaque eum 1accusamus reprehenderit2
illo culpa 12 praesentium1 ea fugit 21hic in vel officiis,
expedita sit 21 et harum enim quaerat, 21 ab corporis quo
atque perspiciatis. Minima odit obcaecati2 1 reiciendis, sed
rerum 2 labore. In fuga, 1 aspernatur earum aliquid 2 121
2commodi delectus?
0 голосов
/ 14 января 2019
tr '\n' $'\x1' |
sed 's/\*\*/\x2/g' |
sed 's/\x2\([^\x2]*\)\x2/1\12/g; s/\x02/1/' |
tr $'\x1' '\n'
  1. Первый tr заменяет символ новой строки на нечитаемый символ, равный шестнадцатеричному числу 0x01.
  2. Тогда первое sed заменяет два ** на гекс 0x02.
  3. Тогда любой шаблон 0x02<anything>0x02 заменяется на 1<anything>2
  4. Последний необъявленный \x02 заменяется на 1.
  5. Затем 0x01 заменяется на новую строку.

Живая версия на tutorialspoint .

Замена * на 0x02 заключается в том, что мы не можем сделать что-то вдоль sed 's/**\(^**\)**/..., т.е. жадное совпадение строки, пока не будет найден шаблон с несколькими символами (или я не знаю, как это сделать). Таким образом, я просто заменяю два символа ** на один не подлежащий изменению символ и обрабатываю его. Таким образом, я могу правильно обрабатывать один *, например **Lor*em ip*sum**.

Если у вас есть GNU sed с опцией -z, замена на новую строку не требуется.

0 голосов
/ 14 января 2019

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

sed -zE 's/(\*\*)([^*]*(\*[^*]+)*)\1/1\22/g' file

Хлопать файл в память. Совпадение **...** и замена переднего ** на 1 и заднего ** на 2 для всего файла.

0 голосов
/ 14 января 2019

Дано:

$ cat file
**Lorem ipsum dolor **sit amet, consectetur adipisicing elit. ** 
Ad velit delectus ** quidem itaque eum **accusamus reprehenderit**
illo culpa **** praesentium** ea fugit ****hic in vel officiis, 
expedita sit **** et harum enim quaerat, **** ab corporis quo 
atque perspiciatis. Minima odit obcaecati** ** reiciendis, sed 
rerum ** labore. In fuga, ** aspernatur earum aliquid ** ****** 
**commodi delectus?

Ваша лучшая ставка - perl или awk:

$ perl -lpE 's/\*\*/$cnt++%2+1/ge' file

Здесь используется способность perl для вычисления выражения с подстановкой.

Метод:

  1. Увеличьте счетчик $cnt++, чтобы получить (0,1,2,3,...) с каждой заменой в s/\*\*/$cnt++%2+1/ge;
  2. Возьмите по модулю %2 этой серии, чтобы получить (0,1,0,1,0...), затем добавьте 1, чтобы получить (1,2,1,2...) для каждой чередующейся замены.

Таким же способом вы можете использовать awk с циклом while:

$ awk '{while (sub(/\*\*/,cnt++%2+1))}1' file

В качестве альтернативы, вы можете slurp весь файл (с -0777), а затем выполнить глобальную замену **[stuff between maybe on multiline]** и заменить на 1[stuff between maybe on multiline]2:

$ perl -0777 -lnE '$s=$_; $s=~s/\*\*([\s\S]*?)\*\*/1${1}2/g; 
                   END{$s=~s/\*\*/1/; say $s;}' file

Обратите внимание на окончательный $s=~s/\*\*/1/; в случае нечетного общего количества замен.

Все три футляра, отпечатки:

1Lorem ipsum dolor 2sit amet, consectetur adipisicing elit. 1 
Ad velit delectus 2 quidem itaque eum 1accusamus reprehenderit2
illo culpa 12 praesentium1 ea fugit 21hic in vel officiis, 
expedita sit 21 et harum enim quaerat, 21 ab corporis quo 
atque perspiciatis. Minima odit obcaecati2 1 reiciendis, sed 
rerum 2 labore. In fuga, 1 aspernatur earum aliquid 2 121 
2commodi delectus?
...