我正在寻找一种方法,将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
提供输出
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
即。 分割 字符序列上的字符串,而不是一个字符串。我怎么能这样做?
我正在寻找一个只会在第二个脚本中修改此行的答案:
IFS='=======' c_split=($c) ;# <---- LINE TO BE CHANGED
答案 0 :(得分:17)
IFS
消歧 IFS
表示输入字段分隔符, list of characters that could be used as separators
。
默认情况下,此设置为
\t\n
,表示空间的任何数字(大于零),制表 和/或 换行符可以一个 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
但是我发现这个解决方案有点矫枉过正。
在bash下,我们可以使用这个基础:
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
Nota:不删除前导和尾随换行符。如果需要,您可以:
mySep=$'\n=======\n'
而不仅仅是=======
。
或者你可以重写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'}"
任何情况下,这个匹配问题的问题(:和他的样本:)
------ 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 。一个%
表示最后一次出现字符串和%%
表示所有出现。返回完整的 variablename 是 string not found。${variablename#*string}
,以相反的方式执行相同操作:返回 variablename 的最后一部分,但不包含 string 。一个#
表示第一次出现和两个##
人所有出现。 Nota替换,字符*
是 joker 表示任意数字的任何字符。
命令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
从第一个mySep
到字符串结尾处将Trunc c 分配到part
。
part="${c%%$mySep*}"
删除主要换行符
part="${part##$'\n'}"
删除尾随换行符并将结果作为新数组元素添加到c_split
。
c_split+=("${part%%$'\n'}")
将 c 重新包含在删除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
答案 1 :(得分:3)
使用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
答案 2 :(得分:1)
在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
或不取决于您的要求。
答案 3 :(得分:1)
当数据包含文字反斜杠序列,空格和其他内容时,这种方法不会发生错误:
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”添加自己的行时会产生额外的空白行)。
答案 4 :(得分:1)
由于此评论,在示例文本中添加了一些内容:
如果用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(OLD,“one line split”):
(我用@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
不换行