在元素中包含空格的Bash数组

时间:2012-01-31 17:40:18

标签: arrays bash unix scripting

我正在尝试使用相机的文件名bash构建一个数组:

FILES=(2011-09-04 21.43.02.jpg
2011-09-05 10.23.14.jpg
2011-09-09 12.31.16.jpg
2011-09-11 08.43.12.jpg)

如您所见,每个文件名中间都有一个空格。

我尝试用引号括起每个名字,然后用反斜杠转义空格,这两种方法都不起作用。

当我尝试访问数组元素时,它继续将空格视为elementdelimiter。

如何正确捕获名称中包含空格的文件名?

10 个答案:

答案 0 :(得分:100)

我认为问题可能部分取决于您访问元素的方式。如果我做一个简单的for elem in $FILES,我会遇到与您相同的问题。但是,如果我通过其索引访问数组,如果我以数字方式或使用转义符添加元素,则它可以工作:

for ((i = 0; i < ${#FILES[@]}; i++))
do
    echo "${FILES[$i]}"
done

$FILES的任何声明都应该有效:

FILES=(2011-09-04\ 21.43.02.jpg
2011-09-05\ 10.23.14.jpg
2011-09-09\ 12.31.16.jpg
2011-09-11\ 08.43.12.jpg)

FILES=("2011-09-04 21.43.02.jpg"
"2011-09-05 10.23.14.jpg"
"2011-09-09 12.31.16.jpg"
"2011-09-11 08.43.12.jpg")

FILES[0]="2011-09-04 21.43.02.jpg"
FILES[1]="2011-09-05 10.23.14.jpg"
FILES[2]="2011-09-09 12.31.16.jpg"
FILES[3]="2011-09-11 08.43.12.jpg"

答案 1 :(得分:81)

访问阵列项目的方式一定有问题。以下是它的完成方式:

for elem in "${files[@]}"
...

来自bash manpage

  

可以使用$ {name [subscript]}引用数组的任何元素。 ...如果下标是@或*,则该单词将扩展为名称的所有成员。这些下标仅在单词出现在双引号内时有所不同。 如果单词是双引号, $ {name [*]}会扩展为单个单词,每个数组成员的值由IFS特殊变量的第一个字符分隔,并且 $ {name [@]}将name的每个元素扩展为单独的单词

当然,访问单个成员时也应该使用双引号

cp "${files[0]}" /tmp

答案 2 :(得分:36)

您需要使用IFS将空格作为元素分隔符停止。

FILES=("2011-09-04 21.43.02.jpg"
       "2011-09-05 10.23.14.jpg"
       "2011-09-09 12.31.16.jpg"
       "2011-09-11 08.43.12.jpg")
IFS=""
for jpg in ${FILES[*]}
do
    echo "${jpg}"
done

如果你想在此基础上分开。然后就做IFS =“。” 希望它可以帮助你:)

答案 3 :(得分:11)

我同意其他人的意见,很可能你是如何访问问题的元素。引用数组赋值中的文件名是正确的:

FILES=(
  "2011-09-04 21.43.02.jpg"
  "2011-09-05 10.23.14.jpg"
  "2011-09-09 12.31.16.jpg"
  "2011-09-11 08.43.12.jpg"
)

for f in "${FILES[@]}"
do
  echo "$f"
done

"${FILES[@]}"形式的任何数组周围使用双引号将数组拆分为每个数组元素一个单词。除此之外,它不会进行任何分词。

使用"${FILES[*]}"也有一个特殊含义,但使用$ IFS的第一个字符连接数组元素,导致一个字,这是可能不是你想要的。

使用裸${array[@]}${array[*]}主题扩展的结果进一步分词,因此您最终会在空格上分割单词(以及$IFS中的任何其他内容)而不是每个数组元素一个单词。

使用C风格的循环也很好,如果你不清楚,可以避免担心分词:

for (( i = 0; i < ${#FILES[@]}; i++ ))
do
  echo "${FILES[$i]}"
done

答案 4 :(得分:2)

逃避工作。

#!/bin/bash

FILES=(2011-09-04\ 21.43.02.jpg
2011-09-05\ 10.23.14.jpg
2011-09-09\ 12.31.16.jpg
2011-09-11\ 08.43.12.jpg)

echo ${FILES[0]}
echo ${FILES[1]}
echo ${FILES[2]}
echo ${FILES[3]}

输出:

$ ./test.sh
2011-09-04 21.43.02.jpg
2011-09-05 10.23.14.jpg
2011-09-09 12.31.16.jpg
2011-09-11 08.43.12.jpg

引用字符串也会产生相同的输出。

答案 5 :(得分:2)

不完全是对原始问题的引用/转义问题的答案,但可能对op来说实际上更有用:

unset FILES
for f in 2011-*.jpg; do FILES+=("$f"); done
echo "${FILES[@]}"

当然,必须根据具体要求采用表达方式(例如,*.jpg表示全部,2001-09-11*.jpg仅表示某一天的图片。

答案 6 :(得分:1)

我曾经重置IFS的值并完成后回滚。

# backup IFS value
O_IFS=$IFS

# reset IFS value
IFS=""

FILES=(
"2011-09-04 21.43.02.jpg"
"2011-09-05 10.23.14.jpg"
"2011-09-09 12.31.16.jpg"
"2011-09-11 08.43.12.jpg"
)

for file in ${FILES[@]}; do
    echo ${file}
done

# rollback IFS value
IFS=${O_IFS}

循环可能的输出:

2011-09-04 21.43.02.jpg

2011-09-05 10.23.14.jpg

2011-09-09 12.31.16.jpg

2011-09-11 08.43.12.jpg

答案 7 :(得分:0)

另一种解决方案是使用&#34;而#34;循环而不是&#34; for&#34;循环:

index=0
while [ ${index} -lt ${#Array[@]} ]
  do
     echo ${Array[${index}]}
     index=$(( $index + 1 ))
  done

答案 8 :(得分:0)

如果您的数组如下所示:     #!/ bin / bash

Unix[0]='Debian'
Unix[1]="Red Hat"
Unix[2]='Ubuntu'
Unix[3]='Suse'

for i in $(echo ${Unix[@]});
    do echo $i;
done

您会得到:

Debian
Red
Hat
Ubuntu
Suse

我不知道为什么,但是循环会分解空格并将它们作为单独的项放置,即使您用引号将其包围。

要解决此问题,您可以调用索引,而不是调用数组中的元素,索引将用引号引起来的完整字符串。 它必须用引号引起来!

#!/bin/bash

Unix[0]='Debian'
Unix[1]='Red Hat'
Unix[2]='Ubuntu'
Unix[3]='Suse'

for i in $(echo ${!Unix[@]});
    do echo ${Unix[$i]};
done

那么您会得到:

Debian
Red Hat
Ubuntu
Suse

答案 9 :(得分:0)

如果您不习惯使用bash,则fish shell的好处之一就是对文件名中的空格进行了不同的处理。考虑一个包含两个文件的目录:“ a b.txt”和“ b c.txt”。在用bash处理从另一个命令生成的文件列表时,这是一个合理的猜测,但是由于您遇到的文件名中的空格而导致失败:

# bash
$ for f in $(ls *.txt); { echo $f; }
a
b.txt
b
c.txt

使用fish时,语法几乎相同,但是结果是您期望的:

# fish
for f in (ls *.txt); echo $f; end
a b.txt
b c.txt

它的工作方式不同,因为fish在换行符(而不是空格)处拆分命令的输出。

如果您确实希望在空格而不是换行符之间进行分割,那么fish的语法很容易理解:

for f in (ls *.txt | string split " "); echo $f; end