在bash中将字符串拆分为数组

use*_*001 8 unix linux bash shell

我正在寻找一种方法来将bash中的字符串拆分为分隔符字符串,并将这些部分放在一个数组中.

简单案例:

#!/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
Run Code Online (Sandbox Code Playgroud)

给出输出

simple string: aaaaa/bbbbb/ddd/ffffff

split
------ new part ------
aaaaa
------ new part ------
bbbbb
------ new part ------
ddd
------ new part ------
ffffff
Run Code Online (Sandbox Code Playgroud)

更复杂的情况:

#!/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
Run Code Online (Sandbox Code Playgroud)

给出输出:

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
Run Code Online (Sandbox Code Playgroud)

我想第二个输出就像

------ new part ------
AA=A
B=BB
------ new part ------
C==CC
DD=D
------ new part ------
EEE
FF
Run Code Online (Sandbox Code Playgroud)

即将字符串拆分为一系列字符,而不是一个字符.我怎样才能做到这一点?

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

IFS='=======' c_split=($c) ;#    <----    LINE TO BE CHANGED 
Run Code Online (Sandbox Code Playgroud)

F. *_*uri 18

IFS 消歧

IFS意思是输入字段分隔符,如list of characters that could be used as separators.

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

所以字符串:

 "    blah  foo=bar 
 baz  "
Run Code Online (Sandbox Code Playgroud)

开头和结尾的分隔符会被忽略,这个字符串将只包含3个部分:blah,foo=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
Run Code Online (Sandbox Code Playgroud)

但这只在字符串不包含时才起作用§.

你可以使用另一个角色,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
Run Code Online (Sandbox Code Playgroud)

但我发现这个解决方案有点矫枉过正.

拆分空间(或不修改IFS)

,我们可以使用这个基础:

b="aaaaa/bbbbb/ddd/ffffff"
b_split=(${b//// })
Run Code Online (Sandbox Code Playgroud)

事实上,这syntaxe ${varname//将启动转换(由分隔/替换出现的所有)/一个空格, before assigning it to an array 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>, 
Run Code Online (Sandbox Code Playgroud)

或交换他们......

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

拆分线strings:

所以,你必须使用IFS你的意思,但是确实有很好的功能:

#!/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"
Run Code Online (Sandbox Code Playgroud)

让我们看看:

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
Run Code Online (Sandbox Code Playgroud)

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

mySep=$'\n=======\n'
Run Code Online (Sandbox Code Playgroud)

而不是简单的=======.

或者你可以重写split循环来明确地保持这个:

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'}"
Run Code Online (Sandbox Code Playgroud)

无论如何,这个匹配的问题是什么(和他的样本:)

------ new part ------
AA=A
B=BB
------ new part ------
C==CC
DD=D
------ last part ------
EEE
FF
Run Code Online (Sandbox Code Playgroud)

最终创造一个 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
Run Code Online (Sandbox Code Playgroud)

这样做很好:

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
Run Code Online (Sandbox Code Playgroud)

一些解释:

  • export -a var定义var为数组并在子项中共享它们
  • ${variablename%string*},${variablename%%string*}导致variablename的左侧部分,upto但没有字符串.一个%意思是字符串的最后一个实例,并%%所有出现.返回完整的variablename是找不到字符串.
  • ${variablename#*string},以相反的方式执行相同的操作:返回变量名的最后一部分但不使用字符串.一个#意思是第一次出现两个##所有出现.

Nota替换,角色*是一个小丑,意味着任何数字的任何角色.

该命令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
Run Code Online (Sandbox Code Playgroud)

所有这些都在联机帮助页中解释:

$ man -Len -Pless\ +/##word bash

$ man -Len -Pless\ +/%%word bash

$ man -Len -Pless\ +/^\\\ *export\\\ .*word bash
Run Code Online (Sandbox Code Playgroud)

分步循环:

分隔符:

mySep=$'======='
Run Code Online (Sandbox Code Playgroud)

声明c_split数组(并且可以与子项共享)

export -a c_split
Run Code Online (Sandbox Code Playgroud)

变量c确实包含至少一个出现的mySep

while [ "$c" != "${c#*$mySep}" ];do
Run Code Online (Sandbox Code Playgroud)

TRUNC Ç从第一mySep结束字符串和指定给part.

    part="${c%%$mySep*}"
Run Code Online (Sandbox Code Playgroud)

删除主要换行符

    part="${part##$'\n'}"
Run Code Online (Sandbox Code Playgroud)

删除尾随换行符并将结果添加为新的数组元素c_split.

    c_split+=("${part%%$'\n'}")
Run Code Online (Sandbox Code Playgroud)

Reassing ç蒙山字符串的其余高达离开时mySep被删除

    c="${c#*$mySep}"
Run Code Online (Sandbox Code Playgroud)

完成;-)

done
Run Code Online (Sandbox Code Playgroud)

删除主要换行符

c=${c##$'\n'}
Run Code Online (Sandbox Code Playgroud)

删除尾随换行符并将结果添加为新的数组元素c_split.

c_split+=("${c%%$'\n'}")
Run Code Online (Sandbox Code Playgroud)

成功:

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"
}
Run Code Online (Sandbox Code Playgroud)

用法:

ssplit "<quoted string>" [array name] [delimiter string]
Run Code Online (Sandbox Code Playgroud)

其中阵列名称$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
Run Code Online (Sandbox Code Playgroud)