Bash 在分隔符上拆分字符串,将段分配给数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15777996/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Bash split string on delimiter, assign segments to array
提问by Robottinosino
In bash, I would like to transform a PATH-like environment variable that maycontain space-separated elements into an array, making sureelements bearing spaces do not cause word-splitting, appearing as "multiple elements".
在bash 中,我想将可能包含空格分隔元素的类似 PATH 的环境变量转换为array,确保带有空格的元素不会导致分词,显示为“多个元素”。
Let PATH_VARIABLEbe the variable in question.
让我们PATH_VARIABLE成为有问题的变量。
Let un:dodecaedro:per:tirare:per:i dannibe the content of the variable.
让un:dodecaedro:per:tirare:per:i danni成为变量的内容。
It isintendedfor the desiredarray_to have 6elements, not 7.
它旨在让所需的array_to 具有6元素,而不是7.
0) un
1) dodecaedro
2) per
3) tirare
4) per
5) i danni
The "tricky" entry may be the space-separated value: i danni.
“棘手”条目可能是空格分隔值:i danni。
I am looking for the absolute most elegantandcorrect way to achieve this.
我正在寻找绝对最优雅和正确的方法来实现这一目标。
Limitation: it must work with my bashversion: v3.2.48(1)-release
限制:它必须适用于我的bash版本:v3.2.48(1)-release
In pythonthis is done just beautifullyas so:
在蟒蛇这只是做了精美的像这样:
>>> v='un:dodecaedro:per:tirare:per:i danni'
>>> len(v.split(':'))
6
Works. Shows what I am looking for.
作品。显示我在寻找什么。
What's the best way to do this in our beloved bash?
在我们心爱的bash 中执行此操作的最佳方法是什么?
Can you specifically improve on my attempt 4?
你能具体改进我的尝试4吗?
Here my attempts
这是我的尝试
#!/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
My environment
我的环境
bashv3.2.48(1)-release
bashv3.2.48(1)-发布
osxOS X v10.8.3 (build 12D78)
osxOS X v10.8.3(构建 12D78)
采纳答案by kojiro
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
Modifying Jim McNamara's answer, you could just reset IFS:
修改 Jim McNamara 的答案,您可以重置 IFS:
oIFS="$IFS"
foo='un:dodecaedro:per:tirare:per:i danni'
IFS=: arr=( $foo )
IFS="$oIFS"
I prefer the function scope because it protects IFS changes from bleeding into the global scope without requiring special care to reset it.
我更喜欢函数作用域,因为它可以防止 IFS 更改渗入全局作用域,而无需特别小心地重置它。
Edits and explanations:
编辑和解释:
As a matter of clarification: In the second example, the IFS setting doeschange the global variable. The salient difference between this:
澄清一下:在第二个示例中,IFS 设置确实更改了全局变量。这之间的显着区别:
IFS=: arr=( $foo )
and this:
和这个:
IFS=: read -a arr <<< "$foo"
is that the former is two variable assignments and no commands, and the latter is a simple command(see simple commandin man (1) bash.)
在于,前者是两个可变分配和没有命令,而后者是一个简单的命令(见简单的命令在man (1) bash)。
Demonstration:
示范:
$ 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"
:
回答by ormaaj
# 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 ]]; then
return 1
elif [[ $- != *f* ]]; then
trap 'trap RETURN; set +f' RETURN
set -f
fi
IFS= command eval "=($)"
}
function main {
typeset -a myPath
if ! stupidSplit myPath PATH :; then
echo "Don't pass stupid stuff to stupidSplit" >&2
return 1
fi
}
main
Rule #1: Don't cram a compound data structure into a string or stream unless there's no alternative. PATHis one case where you have to deal with it.
规则#1:除非别无选择,否则不要将复合数据结构塞进字符串或流中。PATH是您必须处理的一种情况。
Rule #2: Avoid word / field splitting at all costs. There are almost no legitimate reasons to apply word splitting on the value of a parameter in non-minimalist shells such as Bash. Almost all beginner pitfalls can be avoided by just never word splitting with IFS. Always quote.
规则#2:不惜一切代价避免单词/字段拆分。几乎没有正当理由在非极简shell(例如Bash)中对参数值应用分词。几乎所有初学者的陷阱都可以通过从不使用 IFS 进行分词来避免。总是引用。
回答by jim mcnamara
Consider:
考虑:
$ 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
Oh well - took me too long to format an answer... +1 @kojiro.
哦,好吧 - 我花了太长时间来格式化答案...... +1 @kojiro。

