2016-11-18 14 views
1

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

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

ожидается выход:

e 
bb 
aaddsb 
+1

... да? Это не то, что «split» делает вообще. Как и в, ** полностью ** не связано с его фактической функцией. –

+0

Вы хотите, чтобы узнать, как разбить произвольную строку на произвольный многосимвольный разделитель в bash? Почему бы не изменить свой вопрос, чтобы спросить об этом, если это то, что вы действительно хотите знать? –

+0

@CharlesDuffy Итак, что же делать по вашему мнению? – v217

ответ

5

Поскольку вы ждете новой строки, вы можете просто заменить все экземпляры 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 
2

С Баш:

s="emmbbmmaaddsb" 
for i in "${s//mm/$'\n'}"; do echo "$i"; done 

Выход:

 
e 
bb 
aaddsb 
+0

Это не разбивает ничего ... оно заменяет только 'mm' символом новой строки. Вы также можете просто «эхо» $ {s // m/$ '\ n'} "' и полностью вырезать цикл 'for'. –

+0

@gniourf_gniourf: Я предположил, что вопросник хочет что-то сделать с каждой строкой. – Cyrus

+0

Но цикл 'for', как вы его написали, не работает в каждой строке. Он только петли один раз на единственной строке '$ 'e \ nbb \ naaddsb'. –

0

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

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

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

Выход есть:

e 
bb 
aaddsb 
+0

"Рекомендовано"? См. [BashFAQ # 100] (http://mywiki.wooledge.org/BashFAQ/100) для получения наилучших рекомендаций по обработке строк в bash. Вы заметите, что расширение параметра обычно считается наилучшим подходом для коротких входов (в то время как подход «echo | sed», в то время как короткий, имеет большие накладные расходы с точки зрения того, как он реализован под капотом - требуется, как правило, две вилки, mkfifo, 'execv' внешнего инструмента, который должен быть связан и загружен и т. д.). –

+0

... если вы были, например, в строгом цикле, обрабатывая ввод строки за строкой (или повторяя результат glob с сотнями или тысячами имен файлов), вызывая 'echo | sed' для каждой строки будет * абсолютно * быть antipattern. (Вызов 'sed' * только один раз * для обработки всего входящего потока, наоборот, часто является подходящим). –

2

Более общий пример, без замены мульти -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

+0

Это сломано (не удастся, если строка содержит символы или пробелы в глобусе и т. Д.). Более того, вы не используете современные идиомы Bash, что делает код очень странным. Вам нужен только простой цикл: 'str =" LearnABCtoABCSplitABCaABCString "delimiter = ABC s = $ str $ delimiter array =(); while [[$ s]]; do array + = ("$ {s %%" $ delimiter "*}"); s = $ {s # * "$ разделитель"}; сделанный; declare -p array'. Это все. –

+0

Спасибо за комментарий @gniourf_gniourf. Я только что начал с Bash Scripting, и ваше предложение действительно полезно думать в идиоматическом подходе. –

 Смежные вопросы

  • Нет связанных вопросов^_^