介绍
在这个底部,你会发现一个将字符串转换为数组的函数,并且具有以下语法:
ssplit "<string>" "<array name>" "<delimiter string>"
对于这个:
ssplit "$c" c_split $'\n=======\n'
declare -p c_split
declare -a c_split=([0]=$'AA=A\nB=BB' [1]=$'C==CC\nDD=D' [2]=$'EEE\nFF')
IFS
歧义
IFS
的意思是 输入字段分隔符,即可以用作分隔符的字符列表
。
默认情况下,它设置为
\t\n
,这意味着任何数量(大于零)的空格,制表符和/或换行符都可以是一个分隔符
。
因此,对于字符串:$' blah foo=bar \nbaz '
read -a c_split <<<" blah foo=bar
baz "
declare -p c_split
declare -a c_split=([0]="blah" [1]="foo=bar")
领先和尾随的分隔符将被忽略,这个字符串只包含3个部分:
blah
、
foo=bar
和
baz
。但除了空格,IFS会将
每个分隔符视为自己。
IFS=Z read a b c d e f <<<ZaZZbZcZZdZeZf
declare -p a b c d e f
declare -- a=""
declare -- b="a"
declare -- c=""
declare -- d="b"
declare -- e="c"
declare -- f="ZdZeZf"
如果您知道一个在字符串中没有使用的有效字段分隔符,那么使用 IFS
分割字符串是可能的,因此您可以使用 ${var//<pattern>/<separator>}
语法通过此字符替换您的模式:
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>,
在分隔符字符串
上拆分行:
因此,您需要不使用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
关于Leading newline
在之前的示例中,前导和尾随的换行符并不会被删除。为此,您可以简单地执行以下操作:
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
最终创建一个数组
。
#!/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 var
定义 var
为数组,并在子进程中共享它们。
${variablename%string*}
,${variablename%%string*}
返回 variablename 左侧的部分,直到但不包括 string。一个 %
表示 字符串的最后出现,两个 %%
表示 所有出现。如果未找到 string,则返回完整的 variablename。
${variablename#*string}
,以相反的方式执行相同的操作:从 variablename 的末尾返回部分,但不包括 string。一个 #
表示 第一次出现,两个 ##
表示 所有出现。
注意,在替换中,字符 *
是一个 通配符,表示任意数量的任何字符。
命令
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\ +/
$ man -Len -Pless\ +/%%word bash
$ man -Len -Pless\ +/^\\\ *export\\\ .*word bash
逐步分割循环:
分隔符:
mySep=$'======='
声明
c_split
为一个数组(可以与子元素共享)
export -a c_split
当变量c至少包含一个mySep
时
while [ "$c" != "${c#*$mySep}" ];do
从第一个
mySep
截取字符串并将其赋值给
part
。
part="${c%%$mySep*}"
删除前导换行符
part="${part##$'\n'}"
从末尾删除换行符,并将结果作为新的数组元素添加到 c_split 中。
c_split+=("${part%%$'\n'}")
当删除左边到
mySep
的部分时,重新分配
c 和字符串的其余部分。
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