我正在尝试在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)
正如您所看到的,每个文件名中间都有一个空格。
我已经尝试将每个名称用引号括起来,并使用反斜杠转义空格,但两者都不起作用。
当我尝试访问数组元素时,它继续将空格视为元素分隔符。
我该如何正确地捕获带有空格的文件名?
我认为问题可能部分地与您访问元素的方式有关。如果我只是简单地使用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"
echo "${FILES[$i]}"
)。对于 echo
来说没有关系,但对于将其用作文件名的任何内容来说都会有影响。 - Gordon Davissonfor f in "${FILES[@]}"
循环遍历元素时,不必循环遍历索引。 - Mark Edgarfor ((i = 0; i < ${#FILES[@]}; i++))
语句中,井号 (#
) 符号的作用是什么? - Michal Vician你访问数组元素的方式可能有问题。下面是正确的访问方式:
for elem in "${files[@]}"
...
来自Bash手册:
使用${name[subscript]}可以引用数组中的任何元素。... 如果subscript为@或*,则该单词扩展为name的所有成员。只有当单词出现在双引号中时,这些下标才有所不同。如果单词被双引号引起来,${name[*]}会将每个数组成员的值连接成一个单词,并用IFS特殊变量的第一个字符分隔,${name[@]}将name的每个元素扩展为一个独立的单词。
当访问单个成员时,当然也应该使用双引号。
cp "${files[0]}" /tmp
for elem in "${files[@]}"
,而他们使用了 for elem in ${files[@]}
- 所以空格会混淆扩展,导致 for 命令尝试对单独的单词进行操作。 - arntgFILES=("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="."。
IFS=""
。 - pkamb我同意其他人的看法,很可能是您访问元素的方式有问题。在数组赋值中引用文件名是正确的:
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循环也可以:
for (( i = 0; i < ${#FILES[@]}; i++ ))
do
echo "${FILES[$i]}"
done
这个问题之前已经得到了回答,但那个回答有点简短,并且手册摘录有点晦涩难懂。我想提供一个完整的实例来演示如何实际操作。
如果没有加引号,数组就会扩展为由空格分隔的字符串。
for file in ${FILES[@]}; do
扩展为
for file in 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 ; do
但是如果您引用扩展,bash会在每个单元周围添加双引号,因此:
for file in "${FILES[@]}"; do
扩展为
for file in "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" ; do
简单来说,无论何时使用[@]
代替[*]
并在需要保留空格时引用数组扩展是个好习惯。
更进一步地解释一下,另一个回答中的手册说明了如果未被引用,$*
和$@
的行为是相同的,但是它们在被引用时是不同的。因此,考虑到:
array=(a b c)
然后$*
和$@
都被扩展为
a b c
"$*"
会扩展为
"a b c"
"$@"
会被扩展为:
"a" "b" "c"
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
对于那些喜欢使用单行模式设定数组的人,可以使用换行符临时更改 IFS
以避免转义。
OLD_IFS="$IFS"
IFS=$'\n'
array=( $(ls *.jpg) ) #save the hassle to construct filename
IFS="$OLD_IFS"
#! /bin/bash
renditions=(
"640x360 80k 60k"
"1280x720 320k 128k"
"1280x720 320k 128k"
)
for z in "${renditions[@]}"; do
echo "$z"
done
输出
640x360 80k 60k
1280x720 320k 128k
1280x720 320k 128k
`
虽然这并不是对原问题中引用/转义问题的确切答案,但可能更有用于问题提出者的内容:
unset FILES
for f in 2011-*.jpg; do FILES+=("$f"); done
echo "${FILES[@]}"
当然,表达式必须根据具体要求进行调整(例如,对于所有图片使用*.jpg
或仅限某一天的图片使用2001-09-11*.jpg
)。
#!/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
FILES[0] = ...
这样?(编辑:我刚刚试了一下,不起作用。有趣)。 - Dan Fegofind . -maxdepth 1 -type f -iname \*.$1 -printf '%f\n'
); for element in ${array[@]}; do echo $element; done - Alex Hall