2016-10-23 2 views
2

Мне нужна команда sed, которая берет строку и удаляет все копии первого символа с самого начала (но не от остальной части строки).С учетом строки, как удалить пробег первого символа? (sed)

Например, AAABAC должен производить BAC, потому что первая буква A, поэтому мы удаляем весь пробег A с самого начала.

Моя первоначальная мысль была:

data=$(echo $data | sed 's/^.\+\(.*\)/\1/') 

, но это не работает (выводит пустую строку). Если я заменил первый . на определенный символ, он будет успешно работать только для этого символа, но я не могу правильно его подставить.

Я думаю, что . соответствует первому символу, как я хочу, но тогда + не помнит букву, которую я хочу, и продолжает принимать каждый символ до конца строки, так что в круглых скобках ничего нет и поэтому вся строка заменяется ничем. Как я могу сначала принять любой символ, но затем «заблокировать» этот символ для +?

ответ

7

Вы можете использовать:

$> s='AAABAC' 
$> sed -E 's/^(.)\1*//' <<< "$s" 

BAC 
  • (.) будет соответствовать первому характер и захватывает его в группе # 1
  • \1* будет соответствовать 0 или более экземпляров одного и того же характера

В качестве альтернативы he повторно это чистый BASH способ сделать то же самое:

$> shopt -s extglob 
$> echo "${s##+(${s:0:1})}" 

BAC 

${s:0:1} дает нам первый символ $s и ##+(${s:0:1}) удаляет все экземпляры первого символа с самого начала.

+2

спасибо, это * почти * безупречный. Это не работает, когда есть только один из первых символов, но изменение + на a * решает это. – Caleb

+1

Да, это правильно, ответьте на обновления. У меня сложилось впечатление, что вы не хотите удалять, если в начале нет повторов. – anubhava

+1

Красиво сделано; в то время как '-E' часто делает команду' sed' работать как на Linux, так и на macOS, в этом случае это не так, потому что ERE на macOS не поддерживают обратные ссылки. Однако существует решение BRE, совместимое с POSIX: 'sed 's/^ \ (. \) \ 1 * //'' – mklement0

2

Вы можете сделать это с Grep, если ваш Grep понимает Perl совместимых регулярных выражений:

$ grep -Po '^(.)\1*\K.*' <<< 'AABAC' 
BAC 

или

$ grep -Po '^(.)\1*\K.*' <<< 'ABAC' 
BAC 

-o сохраняет только матч, и \K является переменной длиной look- позади, удаляя как можно больше одинаковых символов с начала строки.

2

Bash также поддерживает регулярные выражения:

$ m='(.)(\1+)(.+)'; [[ AAAAABAC =~ $m ]]; printf '%s' "${BASH_REMATCH[3]}" 
BAC 

Действительно для системной библиотеки регулярных выражений GNU ERE (зависит от системы).

3

Для обеспечения дорожной карты к существующим ответам с относительно портативности:

Примечания: Это может быть выведено из синтаксиса, используемого в этом вопросе, и от того, что ответа был принято, что GNUsed используется, но вопрос не помечен как таковой, и он может представлять более широкий интерес.

  • anubhava's helpful answerработает с GNUsed, но не с (более) строго POSIX-совместимых sed реализаций, таких как тот, найденного на MacOS.

  • Benjamin W.'s helpful answer работа с GNUgrep, из-за требующие -P вариант для поддержки PCRE, которые не поддерживают другие grep реализации, например, найденные на MacOS,.

  • soronta's helpful answer работает на платформах, которые используют регулярные выражения библиотеку GNU (в большинстве дистрибутивов Linux), или, в более общем плане, на платформах которых ERE (расширенное регулярное выражение) синтаксис поддерживает обратные ссылки, как нестандартного расширения POSIX spec.

    • Обратите внимание, что =~, оператор регулярного выражения сопоставления в Bash, это одна из редких особенностей Bash, чьи поведение зависит от платформы, в связи с использованием библиотеки регулярных выражений соответствующих платформы.

Вот POSIX-совместимые решения, который должен работать на всех современных Unix-подобных платформах, поскольку она использует Brès (основные регулярные выражения), для которых POSIX делает мандат обратной ссылки поддержка:

$ echo 'AAABAC' | sed 's/^\(.\)\1*//' 
BAC 
+1

Очень хороший POSIX-совместимый ответ ++ – anubhava