Почему не работает следующий код bash?Как расколоть строку на мультисимвольный разделитель в bash?
for i in $(echo "emmbbmmaaddsb" | split -t "mm" )
do
echo "$i"
done
ожидается выход:
e
bb
aaddsb
Почему не работает следующий код bash?Как расколоть строку на мультисимвольный разделитель в bash?
for i in $(echo "emmbbmmaaddsb" | split -t "mm" )
do
echo "$i"
done
ожидается выход:
e
bb
aaddsb
Поскольку вы ждете новой строки, вы можете просто заменить все экземпляры mm
в строке с символом новой строки. В чистом родном Баше:
in='emmbbmmaaddsb'
sep='mm'
printf '%s\n' "${in//$sep/$'\n'}"
Если вы хотите сделать такую замену на более длительный входной потоке, вы можете быть лучше использовать awk
как встроенные строки в Bash не очень хорошо масштабируется для более нескольких килобайт контента. gsub_literal
функция оболочки (backending в awk
) приведены в BashFAQ #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
С Баш:
s="emmbbmmaaddsb"
for i in "${s//mm/$'\n'}"; do echo "$i"; done
Выход:
e bb aaddsb
Это не разбивает ничего ... оно заменяет только 'mm' символом новой строки. Вы также можете просто «эхо» $ {s // m/$ '\ n'} "' и полностью вырезать цикл 'for'. –
@gniourf_gniourf: Я предположил, что вопросник хочет что-то сделать с каждой строкой. – Cyrus
Но цикл 'for', как вы его написали, не работает в каждой строке. Он только петли один раз на единственной строке '$ 'e \ nbb \ naaddsb'. –
Рекомендованный инструмент для подстановки символов - это sed
команда s/regexp/replacement/
для одного регулярного выражения или глобального s/regexp/replacement/g
, вам даже не нужен цикл или переменные.
трубы ваш echo
выход и попытаться заменить символы, mm
witht символ новой строки \n
:
echo "emmbbmmaaddsb" | sed 's/mm/\n/g'
Выход есть:
e
bb
aaddsb
"Рекомендовано"? См. [BashFAQ # 100] (http://mywiki.wooledge.org/BashFAQ/100) для получения наилучших рекомендаций по обработке строк в bash. Вы заметите, что расширение параметра обычно считается наилучшим подходом для коротких входов (в то время как подход «echo | sed», в то время как короткий, имеет большие накладные расходы с точки зрения того, как он реализован под капотом - требуется, как правило, две вилки, mkfifo, 'execv' внешнего инструмента, который должен быть связан и загружен и т. д.). –
... если вы были, например, в строгом цикле, обрабатывая ввод строки за строкой (или повторяя результат glob с сотнями или тысячами имен файлов), вызывая 'echo | sed' для каждой строки будет * абсолютно * быть antipattern. (Вызов 'sed' * только один раз * для обработки всего входящего потока, наоборот, часто является подходящим). –
Более общий пример, без замены мульти -character с одним разделителем символов приведен ниже:
Использование расширений параметров: (из комментария @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 Tutorial - Bash Split String
Это сломано (не удастся, если строка содержит символы или пробелы в глобусе и т. Д.). Более того, вы не используете современные идиомы Bash, что делает код очень странным. Вам нужен только простой цикл: 'str =" LearnABCtoABCSplitABCaABCString "delimiter = ABC s = $ str $ delimiter array =(); while [[$ s]]; do array + = ("$ {s %%" $ delimiter "*}"); s = $ {s # * "$ разделитель"}; сделанный; declare -p array'. Это все. –
Спасибо за комментарий @gniourf_gniourf. Я только что начал с Bash Scripting, и ваше предложение действительно полезно думать в идиоматическом подходе. –
... да? Это не то, что «split» делает вообще. Как и в, ** полностью ** не связано с его фактической функцией. –
Вы хотите, чтобы узнать, как разбить произвольную строку на произвольный многосимвольный разделитель в bash? Почему бы не изменить свой вопрос, чтобы спросить об этом, если это то, что вы действительно хотите знать? –
@CharlesDuffy Итак, что же делать по вашему мнению? – v217