IFS
消歧
IFS
表示输入字段分隔符,如list of characters that could be used as separators
。
默认情况下,这设置为
\t\n
,这意味着任何数量(大于零)的空格、制表 和/或 换行符都可以是1 separator
。
所以字符串:
" blah foo=bar
baz "
前导和尾随分隔符将被忽略,此字符串将仅包含 3 个部分blah
:foo=bar
和baz
。
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 var
定义var
为数组并在孩子中共享
${variablename%string*}
,${variablename%%string*}
导致变量名的左侧部分,最多但没有字符串。一个%
意味着最后一次出现的字符串和所有%%
的出现。返回完整的变量名是未找到字符串。
${variablename#*string}
,以相反的方式执行相同的操作:从但不返回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\ +/##word bash
$ 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
从字符串的第一个到结尾截断cmySep
并分配给part
.
part="${c%%$mySep*}"
删除前导换行符
part="${part##$'\n'}"
删除尾随换行符并将结果作为新数组元素添加到c_split
.
c_split+=("${part%%$'\n'}")
移除剩余的字符串时重新评估cmySep
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