爲什麼不能運行下面的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
所有實例的字符串以換行符。在純天然的bash:
in='emmbbmmaaddsb'
sep='mm'
printf '%s\n' "${in//$sep/$'\n'}"
如果你想要做一個較長的輸入流這樣的替換,你可能會更好使用awk
,如bash的內置字符串操作不能很好地擴展到超過幾千字節的內容。在給出的gsub_literal
外殼函數(backending到awk
)是適用的:
# 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
使用bash:
s="emmbbmmaaddsb"
for i in "${s//mm/$'\n'}"; do echo "$i"; done
輸出:
e bb aaddsb
這不會分割任何東西......它只是用換行符替換「mm」。你可能只需要'echo'$ {s // m/$'\ n'}「'並完全拋棄'for'循環。 –
@gniourf_gniourf:我認爲提問者想要對每一行都做些什麼。 – Cyrus
但是'for'循環,你寫它的方式,不會在每一行循環。它只在單個字符串'$'e \ nbb \ naaddsb'上循環一次。 –
對於一個正則表達式發生或全局s/regexp/replacement/g
,推薦的字符取代工具是sed
的命令s/regexp/replacement/
,您甚至不需要循環或變量。
管你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'每行都會*絕對*成爲反模式。 (相比之下,調用'sed' *一次*來處理整個傳入流通常是合適的)。 –
更普遍的例子,而無需更換多帶有單個字符分隔符的字符分隔符如下所示:
使用參數擴展:(從@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
這被破壞了(如果字符串包含glob字符或空格等,將會失敗)。而且,你並沒有使用現代的Bash成語,這使得代碼看起來很奇怪。你只需要一個簡單的循環:'str =「LearnABCtoABCSplitABCaABCString」delimiter = ABC s = $ str $ delimiter array =();而[[$ s]]; do array + =(「$ {s %%」$ delimiter「*}」); S = $ {S#* 「$分界」};完成; declare -p array'。就這樣。 –
謝謝@gniourf_gniourf的評論。我剛剛開始使用Bash腳本,並且您的建議對於以慣用方式思考非常有幫助。 –
...是吧?這不是'split'所做的。如在**中,完全**與其實際功能無關。 –
你是否想*知道如何在bash中的任意多字符分隔符上分割任意字符串?爲什麼不編輯你的問題,而是問,如果這是你真正想知道的? –
@CharlesDuffy那麼分裂在你看來有什麼作用? – v217