Как я могу заменить несколько экземпляров символа одинаковым количеством экземпляров другого символа в linux sed?

264
xman

Мне нужно заменить повторяющийся набор символов (2 или более) на точное количество заменяющих символов. Мне нужно сделать это либо с помощью sed, либо внутри vi.

Примеры

"abc,,,def" becomes "abc|||dev" "1245d,,,,,22" becomes "1245d|||||22" 

Спасибо

2
Итак, вам нужна глобальная замена персонажа другим в файле? Alex 6 лет назад 0
Да, но только там, где есть 2+ повторения этого. xman 6 лет назад 0
Это может быть легко достигнуто с помощью `perl`:` perl -lape 's /, / "|" x длина ($ &) / ge'`. Жаль, что ты не можешь использовать это (почему?). Его можно вызвать изнутри `vi`, не знаю, допустимо ли это в вашей ситуации. simlev 6 лет назад 0

3 ответа на вопрос

1
jvb

Труба через sed, как

echo "abc,,,def" | sed 's/,/|/g'

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

tr ',' '|'

в этом случае.

Это не будет работать. Как упомянуто в вопросе, оно должно соответствовать 2 или более повторениям персонажа. xman 6 лет назад 0
спасибо за разъяснения, моя вина. Таким образом, вы хотите заменить только части в скобках: echo "a, bc ,,, def" | sed -E 's / (,, +) / (\ 1) / g' -> a, bc (,,,) def - исследуем сейчас ... jvb 6 лет назад 0
частичное "решение": `echo" a, b ,, c ,,, c "| sed 's / ,,,, / |||| / г; с / ,,, / ||| / г; s / ,, / || / g '`(при необходимости разверните шаблон). Не очень хорошее решение, но * может * пригодиться, если известно максимальное количество повторений. jvb 6 лет назад 0
Верно ... ваш подход будет работать, если мы знаем максимальное количество повторений. Было бы хорошо, если есть общее решение, хотя. xman 6 лет назад 0
Как насчет `echo" a, b ,, c ,,, c "| sed -E 's / ([^,]), ([^,]) / \ 1 # \ 2 / г; s /, / | / г; s / # /, / g'`? Это преобразует каждую * одну * "," в "#", затем все оставшиеся (= несколько) "," в "|", а затем все сохраненные "#" в ",". Но для этого нужен символ, который не используется во входном потоке (# здесь). jvb 6 лет назад 0
это будет работать. Вы правы в том, что для этого нужен неиспользованный персонаж ... который, к счастью, доступен. xman 6 лет назад 0
1
Paulo

Извините, что не комментирую, у меня нет 50 репутации.

Это решение не удастся, если есть больше шаблонов, как abc,,,def,g.

sed -n 's/[^,],,/&/;tsubs;p;d;:subs s/,/|/g;p' <<<'abc,,,def abc,,def abc,,,def,g abc,def' 
0
Michael Vehrs

Это практически невозможно из-за работы регулярных выражений. Как уже указывал jvb, решение является простым (хотя и не обязательно коротким), если известно максимальное количество последовательных исходных символов. Если нет, то можно сначала изменить все исходные символы, а затем снова изменить отдельные символы на втором шаге. Однако это работает, только если целевой символ не встречается во входном потоке или если вы можете использовать символ, о котором известно, что он не встречается во входном потоке, в качестве промежуточной цели.

Кроме того, вам нужно принять во внимание угловые случаи одиночного исходного символа, встречающегося в начале или конце строки. Таким образом:

tr ',' '|' < file | sed 's/\([^|]\)|\([^|]\)/\1,\2/;s/^|\([^|]\)/,\1/;s/\([^|]\)|$/\1,/' 

или же

sed 's/,/|/g;s/\([^|]\)|\([^|]\)/\1,\2/;s/^|\([^|]\)/,\1/;s/\([^|]\)|$/\1,/' file 

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