Как разделить строку на многосимвольный разделитель в 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; }'
бб