在bash中,我想将可能包含空格分隔的元素的类似PATH的环境变量转换为array
,使确定元素承载空间不会导致分词,显示为“多个元素”。
让PATH_VARIABLE
成为有问题的变量。
让un:dodecaedro:per:tirare:per:i danni
成为变量的内容。
预期 所需的 array
_拥有6
元素,而不是7
0) un
1) dodecaedro
2) per
3) tirare
4) per
5) i danni
“棘手”条目可能是以空格分隔的值:i danni
。
我正在寻找绝对最优雅的 和正确的方法来实现这一目标。
限制:它必须适用于我的bash版本:v3.2.48(1)-release
在python中,只需精美即可:
>>> v='un:dodecaedro:per:tirare:per:i danni'
>>> len(v.split(':'))
6
作品。显示我在寻找的东西。
在我们心爱的bash中,最好的方法是什么?
您能否专门改进我的尝试4
?
#!/bin/bash
PATH_VARIABLE='un:dodecaedro:per:tirare:per:i danni'
# WRONG
a1=($(echo $PATH_VARIABLE | tr ':' '\n'))
# WRONG
a2=($(
while read path_component; do
echo "$path_component"
done < <(echo "$PATH_VARIABLE" | tr ':' '\n')
))
# WORKS, it is elegant.. but I have no bash 4!
# readarray -t a3 < <(echo "$PATH_VARIABLE" | tr ':' '\n')
# WORKS, but it looks "clunky" to me :(
i=0
while read line; do
a4[i++]=$line
done < <(echo "$PATH_VARIABLE" | tr ':' '\n')
n=${#a4[@]}
for ((i=0; i < n; i++)); do
printf '%2d) %s\n' "$i" "${a4[i]}"
done
bash v3.2.48(1)-release
osx OS X v10.8.3(build 12D78)
答案 0 :(得分:7)
f() {
local IFS=:
local foo
set -f # Disable glob expansion
foo=( $@ ) # Deliberately unquoted
set +f
printf '%d\n' "${#foo[@]}"
printf '%s\n' "${foo[@]}"
}
f 'un:dodecaedro:per:tirare:per:i danni'
6
un
dodecaedro
per
tirare
per
i danni
修改Jim McNamara的答案,你可以重置IFS:
oIFS="$IFS"
foo='un:dodecaedro:per:tirare:per:i danni'
IFS=: arr=( $foo )
IFS="$oIFS"
我更喜欢功能范围,因为它可以保护IFS更改不会流失到全局范围,而无需特别注意重置它。
作为澄清:在第二个示例中,IFS设置 更改全局变量。这之间的显着差异:
IFS=: arr=( $foo )
和此:
IFS=: read -a arr <<< "$foo"
前者是两个变量赋值而没有命令,后者是一个简单的命令(参见man (1) bash
中的简单命令。)
$ echo "$BASH_VERSION"
3.2.48(1)-release
$ echo "$IFS"
$ foo='un:dodecaedro:per:tirare:per:i danni'
$ IFS=: read -a arr <<< "$foo"
$ echo "${#arr[@]}"
6
$ echo "$IFS"
$ IFS=: arr1=( $foo )
$ echo "${#arr1[@]}"
6
$ echo "$IFS"
:
答案 1 :(得分:7)
# Right. Add -d '' if PATH members may contain newlines.
IFS=: read -ra myPath <<<"$PATH"
# Wrong!
IFS=: myPath=($PATH)
# Wrong!
IFS=:
for x in $PATH; do ...
# How to do it wrong right...
# Works around some but not all word split problems
# For portability, some extra wrappers are needed and it's even harder.
function stupidSplit {
if [[ -z $3 ]]; then
return 1
elif [[ $- != *f* ]]; then
trap 'trap RETURN; set +f' RETURN
set -f
fi
IFS=$3 command eval "${1}=(\$${2})"
}
function main {
typeset -a myPath
if ! stupidSplit myPath PATH :; then
echo "Don't pass stupid stuff to stupidSplit" >&2
return 1
fi
}
main
规则#1:除非别无选择,否则不要将复合数据结构塞入字符串或流中。 PATH
是您需要处理的一个案例。
规则#2:不惜一切代价避免单词/字段拆分。几乎没有合理的理由对非极简主义shell(如Bash)中的参数值应用单词拆分。几乎所有的初学者陷阱都可以通过永远不与IFS分词来避免。总是引用。
答案 2 :(得分:5)
考虑:
$ foo='1:2 3:4 5:6'
$ IFS=':'; arr=($foo)
$ echo "${arr[0]}"
1
$ echo "${arr[1]}"
2 3
$ echo "${arr[2]}"
4 5
$ echo "${arr[3]}"
6
哦好吧 - 花了太长时间来格式化答案...... +1 @kojiro。