![使用 IFS 分割字符串](https://linux22.com/image/119410/%E4%BD%BF%E7%94%A8%20IFS%20%E5%88%86%E5%89%B2%E5%AD%97%E7%AC%A6%E4%B8%B2.png)
我已经编写了一个示例脚本来分割字符串,但它没有按预期工作
#!/bin/bash
IN="One-XX-X-17.0.0"
IFS='-' read -r -a ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
echo "Element:$i"
done
#split 17.0.0 into NUM
IFS='.' read -a array <<<${ADDR[3]};
for element in "${array[@]}"
do
echo "Num:$element"
done
- 实际产量
One XX X 17.0.0 17 0 0
- 但我预计输出是:
One XX X 17.0.0 17 0 0
答案1
在旧版本中,bash
您必须在 后引用变量<<<
。这个问题在 4.4 中得到了修复。在旧版本中,变量将在 IFS 上拆分,并在存储在构成该<<<
重定向的临时文件中之前在空间上连接生成的单词。
在 4.2 及之前的版本中,当重定向诸如read
或 之类的内置函数时command
,该拆分甚至会采用该内置函数的 IFS(4.3 修复了该问题):
$ bash-4.2 -c 'a=a.b.c.d; IFS=. read x <<< $a; echo "$x"'
a b c d
$ bash-4.2 -c 'a=a.b.c.d; IFS=. cat <<< $a'
a.b.c.d
$ bash-4.2 -c 'a=a.b.c.d; IFS=. command cat <<< $a'
a b c d
4.3 中修复了这个问题:
$ bash-4.3 -c 'a=a.b.c.d; IFS=. read x <<< $a; echo "$x"'
a.b.c.d
但$a
仍然受到分词的影响:
$ bash-4.3 -c 'a=a.b.c.d; IFS=.; read x <<< $a; echo "$x"'
a b c d
在 4.4 中:
$ bash-4.4 -c 'a=a.b.c.d; IFS=.; read x <<< $a; echo "$x"'
a.b.c.d
为了移植到旧版本,请引用您的变量(或首先使用zsh
该变量的来源并且不存在该问题)<<<
$ bash-any-version -c 'a=a.b.c.d; IFS=.; read x <<< "$a"; echo "$x"'
a.b.c.d
请注意,这种分割字符串的方法仅适用于不包含换行符的字符串。另请注意,它将a..b.c.
被拆分为"a"
, ""
, "b"
, "c"
(最后一个元素没有空)。
要分割任意字符串,您可以使用 split+glob 运算符(这将使其成为标准并避免像以前那样将变量的内容存储在临时文件中<<<
):
var='a.new
line..b.c.'
set -o noglob # disable glob
IFS=.
set -- $var'' # split+glob
for i do
printf 'item: <%s>\n' "$i"
done
或者:
array=($var'') # in shells with array support
这''
是为了保留尾随的空元素(如果有)。这也会将一个空元素拆分$var
为一个空元素。
或者使用带有适当拆分运算符的 shell:
zsh
:array=(${(s:.:)var} # removes empty elements array=("${(@s:.:)var}") # preserves empty elements
rc
:array = ``(.){printf %s $var} # removes empty elements
fish
set array (string split . -- $var) # not for multiline $var
答案2
修复,(另请参阅S.查泽拉斯的回答用于背景),具有合理的输出:
#!/bin/bash
IN="One-XX-X-17.0.0"
IFS='-' read -r -a ADDR <<< "$IN"
for i in "${ADDR[@]}"; do
if [ "$i" = "${i//.}" ] ; then
echo "Element:$i"
continue
fi
# split 17.0.0 into NUM
IFS='.' read -a array <<< "$i"
for element in "${array[@]}" ; do
echo "Num:$element"
done
done
输出:
Element:One
Element:XX
Element:X
Num:17
Num:0
Num:0
笔记:
最好加上条件第二名环形在这第一名环形。
bash
模式替换 ( ) 检查元素中"${i//.}"
是否有 a 。.
(case
声明可能更简单,尽管与OP的代码。)read
$array
通过输入进行ing<<< "${ADDR[3]}"
不如 ing 通用<<< "$i"
。它避免了需要知道哪个元素有.
s。该代码假设打印“元素:17.0.0“是无意的。如果那种行为是有意将主循环替换为:
for i in "${ADDR[@]}"; do echo "Element:$i" if [ "$i" != "${i//.}" ] ; then # split 17.0.0 into NUM IFS='.' read -a array <<< "$i" for element in "${array[@]}" ; do echo "Num:$element" done fi done
答案3
和awk这将花费你一行:
IN="One-XX-X-17.0.0"
awk -F'[-.]' '{ for(i=1;i<=NF;i++) printf "%s : %s\n",($i~/^[0-9]+$/?"Num":"Element"),$i }' <<<"$IN"
-F'[-.]'
-
- 在我们的例子中基于多个字符的字段分隔符.
输出:
Element : One
Element : XX
Element : X
Num : 17
Num : 0
Num : 0
答案4
这是我的方式:
OIFS=$IFS
IFS='-'
IN="One-XX-X-17.0.0"
ADDR=($IN)
for i in "${ADDR[@]}"; do
echo "Element:$i"
done
IFS='.'
array=(${ADDR[3]})
for element in "${array[@]}"
do
echo "Num:$element"
done
结果符合预期:
Num:17
Num:0
Num:0