use*_*232 11 bash shell-script lfs
我编写了一个示例脚本来拆分字符串,但它没有按预期工作
#!/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
Run Code Online (Sandbox Code Playgroud)
输出
One
XX
X
17.0.0
17 0 0
Run Code Online (Sandbox Code Playgroud)
但我预计输出是:
One
XX
X
17.0.0
17
0
0
Run Code Online (Sandbox Code Playgroud)
Sté*_*las 11
在旧版本中,bash
您必须在<<<
. 这在 4.4 中已修复。在旧版本中,变量将在 IFS 上拆分,结果单词在空间上连接,然后存储在构成该<<<
重定向的临时文件中。
在 4.2 及之前,当重定向类似read
or 的内置函数时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
Run Code Online (Sandbox Code Playgroud)
4.3 中修复的那个:
$ bash-4.3 -c 'a=a.b.c.d; IFS=. read x <<< $a; echo "$x"'
a.b.c.d
Run Code Online (Sandbox Code Playgroud)
但$a
仍然会在那里进行分词:
$ bash-4.3 -c 'a=a.b.c.d; IFS=.; read x <<< $a; echo "$x"'
a b c d
Run Code Online (Sandbox Code Playgroud)
在 4.4:
$ bash-4.4 -c 'a=a.b.c.d; IFS=.; read x <<< $a; echo "$x"'
a.b.c.d
Run Code Online (Sandbox Code Playgroud)
为了可移植到旧版本,引用您的变量(或首先使用zsh
它<<<
来自哪里并且没有那个问题)
$ bash-any-version -c 'a=a.b.c.d; IFS=.; read x <<< "$a"; echo "$x"'
a.b.c.d
Run Code Online (Sandbox Code Playgroud)
请注意,这种拆分字符串的方法仅适用于不包含换行符的字符串。另请注意,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
Run Code Online (Sandbox Code Playgroud)
或者:
array=($var'') # in shells with array support
Run Code Online (Sandbox Code Playgroud)
该''
是维护如有尾随空元素。这也会将$var
一个空元素拆分为一个空元素。
或者使用带有适当拆分运算符的外壳:
zsh
:
array=(${(s:.:)var} # removes empty elements
array=("${(@s:.:)var}") # preserves empty elements
Run Code Online (Sandbox Code Playgroud)rc
:
array = ``(.){printf %s $var} # removes empty elements
Run Code Online (Sandbox Code Playgroud)fish
set array (string split . -- $var) # not for multiline $var
Run Code Online (Sandbox Code Playgroud)修复(另请参阅S. Chazelas 的背景答案),并提供合理的输出:
#!/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
Run Code Online (Sandbox Code Playgroud)
输出:
Element:One
Element:XX
Element:X
Num:17
Num:0
Num:0
Run Code Online (Sandbox Code Playgroud)
笔记:
最好将条件第二个循环放在第一个循环中。
bash
模式替换 ( ) 检查元素中"${i//.}"
是否有 a 。.
(case
语句可能更简单,尽管与OP的代码不太相似。)
read
$array
通过输入进行ing<<< "${ADDR[3]}"
不如 ing 通用<<< "$i"
。它避免了需要知道哪个元素有.
s。
该代码假定打印“ Element: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
Run Code Online (Sandbox Code Playgroud)