13

中,我想将可能包含空格分隔元素的类似 PATH 的环境变量转换为array确保带有空格的元素不会导致分词,显示为“多个元素”。


PATH_VARIABLE成为有问题的变量。

un:dodecaedro:per:tirare:per:i danni为变量的内容。

适用 于所需的 array_to6元素,而不是7.

0) un
1) dodecaedro
2) per
3) tirare
4) per
5) i danni

“棘手”条目可能是空格分隔的值:i danni.

我正在寻找实现这一目标的绝对最优雅 正确的方法。

限制:它必须适用于我的版本:v3.2.48(1)-release


中,这样做非常漂亮

>>> v='un:dodecaedro:per:tirare:per:i danni'
>>> len(v.split(':'))
6

作品。显示我在寻找什么。


在我们心爱的中执行此操作的最佳方法是什么?

你能具体改进我的尝试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

我的环境

v3.2.48(1)-发布

OS X v10.8.3 (build 12D78)


4

3 回答 3

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"
:
于 2013-04-03T03:19:49.753 回答
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:不惜一切代价避免单词/字段拆分。几乎没有正当理由对非极简外壳(如 Bash)中的参数值应用分词。几乎所有初学者的陷阱都可以通过从不使用 IFS 进行分词来避免。总是引用。

于 2013-04-03T21:20:28.403 回答
6

考虑:

$ 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。

于 2013-04-03T03:29:16.940 回答