Как разделить строку на многосимвольный разделитель в bash?

Почему не работает следующий код bash?

for i in $( echo "emmbbmmaaddsb" | split -t "mm"  )
do
    echo "$i"
done

ожидаемый результат:

e
bb
aaddsb

4 ответа

Решение

Так как вы ожидаете перевода строки, вы можете просто заменить все экземпляры mm в вашей строке с новой строкой. В чистом родном bash:

in='emmbbmmaaddsb'
sep='mm'
printf '%s\n' "${in//$sep/$'\n'}"

Если вы хотите сделать такую ​​замену в более длинном входном потоке, вам лучше использовать awkТак как встроенные в bash манипуляции со строками плохо масштабируются до нескольких килобайт контента. gsub_literal функция оболочки (backending в awk) данное в BASFAQ #21 применимо:

# Taken from http://mywiki.wooledge.org/BashFAQ/021

# usage: gsub_literal STR REP
# replaces all instances of STR with REP. reads from stdin and writes to stdout.
gsub_literal() {
  # STR cannot be empty
  [[ $1 ]] || return

  # string manip needed to escape '\'s, so awk doesn't expand '\n' and such
  awk -v str="${1//\\/\\\\}" -v rep="${2//\\/\\\\}" '
    # get the length of the search string
    BEGIN {
      len = length(str);
    }

    {
      # empty the output string
      out = "";

      # continue looping while the search string is in the line
      while (i = index($0, str)) {
        # append everything up to the search string, and the replacement string
        out = out substr($0, 1, i-1) rep;

        # remove everything up to and including the first instance of the
        # search string from the line
        $0 = substr($0, i + len);
      }

      # append whatever is left
      out = out $0;

      print out;
    }
  '
}

... используется в этом контексте как:

gsub_literal "mm" $'\n' <your-input-file.txt >your-output-file.txt

Рекомендуемый инструмент для замены персонажа sedкоманда s/regexp/replacement/ для одного случая регулярного выражения или глобального s/regexp/replacement/gВам даже не нужен цикл или переменные.

Труба твоя echo выведите и попробуйте заменить символы mm с символом новой строки \n:

echo "emmbbmmaaddsb" | sed 's/mm/\n/g'

Выход:

e
bb
aaddsb

Более общий пример, без замены многосимвольного разделителя одним разделителем символов приведен ниже:

Использование расширений параметров: (из комментария @gniourf_gniourf)

#!/bin/bash

str="LearnABCtoABCSplitABCaABCString"
delimiter=ABC
s=$str$delimiter
array=();
while [[ $s ]]; do
    array+=( "${s%%"$delimiter"*}" );
    s=${s#*"$delimiter"};
done;
declare -p array

Более грубый способ

#!/bin/bash

# main string
str="LearnABCtoABCSplitABCaABCString"

# delimiter string
delimiter="ABC"

#length of main string
strLen=${#str}
#length of delimiter string
dLen=${#delimiter}

#iterator for length of string
i=0
#length tracker for ongoing substring
wordLen=0
#starting position for ongoing substring
strP=0

array=()
while [ $i -lt $strLen ]; do
    if [ $delimiter == ${str:$i:$dLen} ]; then
        array+=(${str:strP:$wordLen})
        strP=$(( i + dLen ))
        wordLen=0
        i=$(( i + dLen ))
    fi
    i=$(( i + 1 ))
    wordLen=$(( wordLen + 1 ))
done
array+=(${str:strP:$wordLen})

declare -p array

Ссылка - Учебник Bash - Строка Bash Split

С помощью awk вы можете использовать gsub для замены всех регулярных выражений.

Как и в вашем вопросе, чтобы заменить все подстроки двух или более символов 'm' новой строкой, запустите:

echo "emmbbmmaaddsb" | awk '{ gsub(/mm+/, "\n" ); print; }'

е

бб

aaddsb

"G" в gsub() означает "глобальный", что означает замену везде.

Вы также можете попросить напечатать только N match, например:

echo "emmbbmmaaddsb" | awk '{ gsub(/mm+/, " " ); print $2; }'

бб

Другие вопросы по тегам