将string拆分成bash中的数组

我正在寻找一种方法来在bash中分隔string中的string,并将这些部分放在一个数组中。

简单案例:

#!/bin/bash b="aaaaa/bbbbb/ddd/ffffff" echo "simple string: $b" IFS='/' b_split=($b) echo ; echo "split" for i in ${b_split[@]} do echo "------ new part ------" echo "$i" done 

给出输出

 simple string: aaaaa/bbbbb/ddd/ffffff split ------ new part ------ aaaaa ------ new part ------ bbbbb ------ new part ------ ddd ------ new part ------ ffffff 

更复杂的情况:

 #!/bin/bash c=$(echo "AA=A"; echo "B=BB"; echo "======="; echo "C==CC"; echo "DD=D"; echo "======="; echo "EEE"; echo "FF";) echo "more complex string" echo "$c"; echo ; echo "split"; IFS='=======' c_split=($c) ;# <---- LINE TO BE CHANGED for i in ${c_split[@]} do echo "------ new part ------" echo "$i" done 

给出输出:

 more complex string AA=A B=BB ======= C==CC DD=D ======= EEE FF split ------ new part ------ AA ------ new part ------ A B ------ new part ------ BB ------ new part ------ ------ new part ------ ------ new part ------ ------ new part ------ ------ new part ------ ------ new part ------ ------ new part ------ C ------ new part ------ ------ new part ------ CC DD ------ new part ------ D ------ new part ------ ------ new part ------ ------ new part ------ ------ new part ------ ------ new part ------ ------ new part ------ ------ new part ------ EEE FF 

我想第二个输出是像

 ------ new part ------ AA=A B=BB ------ new part ------ C==CC DD=D ------ new part ------ EEE FF 

也就是string分割一系列字符 ,而不是一个。 我怎样才能做到这一点?

我正在寻找一个答案,只会修改第二个脚本中的这一行:

 IFS='=======' c_split=($c) ;# <---- LINE TO BE CHANGED 

Solutions Collecting From Web of "将string拆分成bash中的数组"

IFS消歧

IFS意味着输入字段分隔符 ,作为list of characters that could be used as separators

默认情况下,这被设置为 \t\n ,意思是任何数字(大于零)的空格制表 和/或 换行符都可以是一个 separator

所以字符串:

  " blah foo=bar baz " 

前后分隔符将被忽略,该字符串将只包含3个部分: blahfoo=barbaz

如果您知道字符串中没有使用有效的字段分隔符,则可以使用IFS拆分字符串。

 OIFS="$IFS" IFS='§' c=$'AA=A\nB=BB\n=======\nC==CC\nDD=D\n=======\nEEE\nFF' c_split=(${c//=======/§}) IFS="$OIFS" printf -- "------ new part ------\n%s\n" "${c_split[@]}" ------ new part ------ AA=A B=BB ------ new part ------ C==CC DD=D ------ new part ------ EEE FF 

但是,这个工作只有当字符串不包含§

你可以使用另一个字符,如IFS=$'\026';c_split=(${c//=======/$'\026'})但无论如何,这可能涉及更多的错误。

你可以浏览人物地图,找出不在你的字符串中的人:

 myIfs="" for i in {1..255};do printf -v char "$(printf "\\\%03o" $i)" [ "$c" == "${c#*$char}" ] && myIfs="$char" && break done if ! [ "$myIFS" ] ;then echo no split char found, could not do the job, sorry. exit 1 fi 

但我觉得这个解决方案有点矫枉过正。

拆分空间(或不修改IFS)

在bash下 ,我们可以使用这种bashism:

 b="aaaaa/bbbbb/ddd/ffffff" b_split=(${b//// }) 

实际上,这个语法${varname//将会启动一个翻译(用/分隔)来替换空间的所有出现/ 然后将其分配给数组b_split

当然,这仍然使用IFS和空间分割数组。

这不是最好的方法,但可以与具体案件。

你甚至可以在分裂之前丢弃不需要的空间:

 b='12 34 / 1 3 5 7 / ab' b1=${b// } b_split=(${b1//// }) printf "<%s>, " "${b_split[@]}" ;echo <12>, <34>, <1>, <3>, <5>, <7>, <ab>, 

或交换他们…

 b1=${b// /§} b_split=(${b1//// }) printf "<%s>, " "${b_split[@]//§/ }" ;echo <12 34 >, < 1 3 5 7 >, < ab>, 

strings分割线:

所以你不得不使用IFS来表达你的意思,但是bash确实有很好的特性:

 #!/bin/bash c=$'AA=A\nB=BB\n=======\nC==CC\nDD=D\n=======\nEEE\nFF' echo "more complex string" echo "$c"; echo ; echo "split"; mySep='=======' while [ "$c" != "${c#*$mySep}" ];do echo "------ new part ------" echo "${c%%$mySep*}" c="${c#*$mySep}" done echo "------ last part ------" echo "$c" 

让我们看看:

 more complex string AA=A B=BB ======= C==CC DD=D ======= EEE FF split ------ new part ------ AA=A B=BB ------ new part ------ C==CC DD=D ------ last part ------ EEE FF 

注意:前导和尾随的换行符不会被删除。 如果需要,您可以:

 mySep=$'\n=======\n' 

而不是简单=======

或者你可以重写分割循环以明确地保持这一点:

 mySep=$'=======' while [ "$c" != "${c#*$mySep}" ];do echo "------ new part ------" part="${c%%$mySep*}" part="${part##$'\n'}" echo "${part%%$'\n'}" c="${c#*$mySep}" done echo "------ last part ------" c=${c##$'\n'} echo "${c%%$'\n'}" 

任何情况下,这匹配什么SO问题要求(和他的样本:)

 ------ new part ------ AA=A B=BB ------ new part ------ C==CC DD=D ------ last part ------ EEE FF 

最后创建一个array

 #!/bin/bash c=$'AA=A\nB=BB\n=======\nC==CC\nDD=D\n=======\nEEE\nFF' echo "more complex string" echo "$c"; echo ; echo "split"; mySep=$'=======' export -a c_split while [ "$c" != "${c#*$mySep}" ];do part="${c%%$mySep*}" part="${part##$'\n'}" c_split+=("${part%%$'\n'}") c="${c#*$mySep}" done c=${c##$'\n'} c_split+=("${c%%$'\n'}") for i in "${c_split[@]}" do echo "------ new part ------" echo "$i" done 

做得很好:

 more complex string AA=A B=BB ======= C==CC DD=D ======= EEE FF split ------ new part ------ AA=A B=BB ------ new part ------ C==CC DD=D ------ new part ------ EEE FF 

一些解释:

  • export -a varvar定义为一个数组,并将它们分享给childs
  • ${variablename%string*}${variablename%%string*}导致variablename的左边部分,直到但没有字符串 。 一个%意味着最后一次出现的字符串%% 所有的出现 。 完整的变量名是返回的字符串未找到。
  • ${variablename#*string} ,以相反的方式执行:返回variablename的最后一部分,但是没有字符串 。 其中#表示第一次出现 ,两次出现。

注意替换字符*是一个小丑意味着任何字符的任何数字。

命令echo "${c%%$'\n'}"将回显变量c,但在字符串末尾没有任何数量的换行符。

所以如果变量包含Hello WorldZorGluBHello youZorGluBI'm happy

 variable="Hello WorldZorGluBHello youZorGluBI'm happy" $ echo ${variable#*ZorGluB} Hello youZorGlubI'm happy $ echo ${variable##*ZorGluB} I'm happy $ echo ${variable%ZorGluB*} Hello WorldZorGluBHello you $ echo ${variable%%ZorGluB*} Hello World $ echo ${variable%%ZorGluB} Hello WorldZorGluBHello youZorGluBI'm happy $ echo ${variable%happy} Hello WorldZorGluBHello youZorGluBI'm $ echo ${variable##* } happy 

所有这一切都在手册中解释:

 $ man -Len -Pless\ +/##word bash $ man -Len -Pless\ +/%%word bash $ man -Len -Pless\ +/^\\\ *export\\\ .*word bash 

一步一步,分裂循环:

分隔符:

 mySep=$'=======' 

c_split声明为一个数组 (并且可以和childs共享)

 export -a c_split 

虽然变量c至少包含一次mySep

 while [ "$c" != "${c#*$mySep}" ];do 

从第一个mySep截断c到字符串的结尾并分配给part

  part="${c%%$mySep*}" 

删除主要的换行符

  part="${part##$'\n'}" 

除去结尾的换行符,并将结果作为新的数组元素添加到c_split

  c_split+=("${part%%$'\n'}") 

把剩下的字符串mySep中去掉

  c="${c#*$mySep}" 

完成;-)

 done 

删除主要的换行符

 c=${c##$'\n'} 

除去结尾的换行符,并将结果作为新的数组元素添加到c_split

 c_split+=("${c%%$'\n'}") 

进入功能:

 ssplit() { local string="$1" array=${2:-ssplited_array} delim="${3:- }" pos=0 while [ "$string" != "${string#*$delim}" ];do printf -v $array[pos++] "%s" "${string%%$delim*}" string="${string#*$delim}" done printf -v $array[pos] "%s" "$string" } 

用法:

 ssplit "<quoted string>" [array name] [delimiter string] 

其中数组名称默认为$splitted_array分隔符是单个空格。

你可以使用:

 c=$'AA=A\nB=BB\n=======\nC==CC\nDD=D\n=======\nEEE\nFF' ssplit "$c" c_split $'\n=======\n' printf -- "--- part ----\n%s\n" "${c_split[@]}" --- part ---- AA=A B=BB --- part ---- C==CC DD=D --- part ---- EEE FF 

用awk来做:

  awk -vRS='\n=*\n' '{print "----- new part -----";print}' <<< $c 

输出:

 kent$ awk -vRS='\n=*\n' '{print "----- new part -----";print}' <<< $c ----- new part ----- AA=A B=BB ----- new part ----- C==CC DD=D ----- new part ----- EEE FF 

以下在bash中测试脚本:

 kent@7pLaptop:/tmp/test$ bash --version GNU bash, version 4.2.42(2)-release (i686-pc-linux-gnu) 

脚本:(命名为t.sh

 #!/bin/bash c=$(echo "AA=A"; echo "B=BB"; echo "======="; echo "C==CC"; echo "DD=D"; echo "======="; echo "EEE"; echo "FF";) echo "more complex string" echo "$c" echo "split now" c_split=($(echo "$c"|awk -vRS="\n=*\n" '{gsub(/\n/,"\\n");printf $0" "}')) for i in ${c_split[@]} do echo "---- new part ----" echo -e "$i" done 

输出:

 kent@7pLaptop:/tmp/test$ ./t.sh more complex string AA=A B=BB ======= C==CC DD=D ======= EEE FF split now ---- new part ---- AA=A B=BB ---- new part ---- C==CC DD=D ---- new part ---- EEE FF 

注意在for循环中的echo语句,如果你删除选项-e你会看到:

 ---- new part ---- AA=A\nB=BB ---- new part ---- C==CC\nDD=D ---- new part ---- EEE\nFF\n 

采取-e或不取决于您的要求。

这里有一个方法,当数据包含文字反斜杠序列,空格和其他数据时,

 c=$(echo "AA=A"; echo "B=BB"; echo "======="; echo "C==CC"; echo "DD=D"; echo "======="; echo "EEE"; echo "FF";) echo "more complex string" echo "$c"; echo ; echo "split"; c_split=() while IFS= read -r -d '' part do c_split+=( "$part" ) done < <(printf "%s" "$c" | sed -e 's/=======/\x00/g') c_split+=( "$part" ) for i in "${c_split[@]}" do echo "------ new part ------" echo "$i" done 

请注意,字符串实际上是根据请求分割为“=======”,因此换行成为数据的一部分(当“echo”添加它时会导致额外的空白行)。

在示例文本中添加了一些,因为这个评论:

如果用AA = A或用AA = \ nA替换AA = A – 这个人就会中断

编辑:我添加了一个建议,对文本中的一些分隔符不敏感。 然而,这不是使用OP所要求的“单行分裂”,但是这是我应该做的, 如果我在bash中做,并且想要结果在一个数组中。

script.sh(新):

 #!/bin/bash text=$( echo "AA=A"; echo "AA =A"; echo "AA=\nA"; echo "B=BB"; echo "======="; echo "C==CC"; echo "DD=D"; echo "======="; echo "EEE"; echo "FF"; ) echo "more complex string" echo "$text" echo "split now" c_split[0]="" current="" del="" ind=0 # newline newl=$'\n' # Save IFS (not necessary when run as sub shell) saveIFS="$IFS" IFS="$newl" for row in $text; do if [[ $row =~ ^=+$ ]]; then c_split[$ind]="$current" ((ind++)) current="" # Avoid preceding newline del="" continue fi current+="$del$row" del="$newl" done # Restore IFS IFS="$saveIFS" # If there is a last poor part of the text if [[ -n $current ]]; then c_split[$ind]="$current" fi # The result is an array for i in "${c_split[@]}" do echo "---- new part ----" echo "$i" done 

script.sh(老,与“一行分裂”):
(我用@Kent的 awk把这个想法搞定了,稍微调整了一下)

 #!/bin/bash c=$( echo "AA=A"; echo "AA =A"; echo "AA=\nA"; echo "B=BB"; echo "======="; echo "C==CC"; echo "DD=D"; echo "======="; echo "EEE"; echo "FF"; ) echo "more complex string" echo "$c" echo "split now" # Now, this will be almost absolute secure, # perhaps except a direct hit by lightning. del="" for ch in $'\1' $'\2' $'\3' $'\4' $'\5' $'\6' $'\7'; do if [ -z "`echo "$c" | grep "$ch"`" ]; then del="$ch" break fi done if [ -z "$del" ]; then echo "Sorry, all this testing but no delmiter to use..." exit 1 fi IFS="$del" c_split=($(echo "$c" | awk -vRS="\n=+\n" -vORS="$del" '1')) for i in ${c_split[@]} do echo "---- new part ----" echo "$i" done 

输出:

 [244an]$ bash --version GNU bash, version 4.2.24(1)-release (x86_64-pc-linux-gnu) [244an]$ ./script.sh more complex string AA=A AA =A AA=\nA B=BB ======= C==CC DD=D ======= EEE FF split now ---- new part ---- AA=A AA =A AA=\nA B=BB ---- new part ---- C==CC DD=D ---- new part ---- EEE FF 

使用-e作为echo ,使AA=\\nA不执行换行