元素中带有空格的 Bash 数组
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9084257/
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 array with spaces in elements
提问by abelenky
I'm trying to construct an array in bash of the filenames from my camera:
我正在尝试用我的相机中的文件名在 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)
As you can see, there is a space in the middle of each filename.
如您所见,每个文件名中间都有一个空格。
I've tried wrapping each name in quotes, and escaping the space with a backslash, neither of which works.
我试过用引号将每个名称括起来,并用反斜杠转义空格,这两种方法都不起作用。
When I try to access the array elements, it continues to treat the space as the elementdelimiter.
当我尝试访问数组元素时,它继续将空格视为元素分隔符。
How can I properly capture the filenames with a space inside the name?
如何正确捕获名称中带有空格的文件名?
采纳答案by Dan Fego
I think the issue might be partly with how you're accessing the elements. If I do a simple for elem in $FILES
, I experience the same issue as you. However, if I access the array through its indices, like so, it works if I add the elements either numerically or with escapes:
我认为问题可能部分与您访问元素的方式有关。如果我做一个简单的for elem in $FILES
,我会遇到和你一样的问题。但是,如果我通过它的索引访问数组,就像这样,如果我以数字或转义方式添加元素,它就会起作用:
for ((i = 0; i < ${#FILES[@]}; i++))
do
echo "${FILES[$i]}"
done
Any of these declarations of $FILES
should work:
这些声明中的任何一个都$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)
or
或者
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")
or
或者
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"
回答by user123444555621
There must be something wrong with the way you access the array's items. Here's how it's done:
您访问数组项的方式一定有问题。这是它的完成方式:
for elem in "${files[@]}"
...
From the bash manpage:
Any element of an array may be referenced using ${name[subscript]}. ... If subscript is @ or *, the word expands to all members of name. These subscripts differ only when the word appears within double quotes. If the word is double-quoted,${name[*]} expands to a single word with the value of each array member separated by the first character of the IFS special variable, and ${name[@]} expands each element of name to a separate word.
可以使用 ${name[subscript]} 引用数组的任何元素。... 如果下标是@ 或*,则单词扩展为name 的所有成员。这些下标仅在单词出现在双引号内时才不同。如果单词是双引号,则${name[*]} 扩展为单个单词,每个数组成员的值由 IFS 特殊变量的第一个字符分隔,${name[@]} 扩展每个元素名称要单独一个字。
Of course, you should also use double quotes when accessing a single member
当然,访问单个成员时也应该使用双引号
cp "${files[0]}" /tmp
回答by Khushneet
You need to use IFS to stop space as element delimiter.
您需要使用 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
If you want to separate on basis of . then just do IFS="." Hope it helps you:)
如果要基于 . 然后就做 IFS="." 希望对你有帮助:)
回答by Dean Hall
I agree with others that it's likely how you're accessing the elements that is the problem. Quoting the file names in the array assignment is correct:
我同意其他人的看法,这很可能是您访问有问题的元素的方式。在数组赋值中引用文件名是正确的:
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
Using double quotes around any array of the form "${FILES[@]}"
splits the array into one word per array element. It doesn't do any word-splitting beyond that.
在任何形式"${FILES[@]}"
的数组周围使用双引号会将数组拆分为每个数组元素一个单词。除此之外,它不会进行任何分词。
Using "${FILES[*]}"
also has a special meaning, but it joinsthe array elements with the first character of $IFS, resulting in oneword, which is probably not what you want.
using"${FILES[*]}"
也有特殊含义,但是它将数组元素与 $IFS 的第一个字符连接起来,产生一个单词,这可能不是您想要的。
Using a bare ${array[@]}
or ${array[*]}
subjects the result of that expansion to further word-splitting, so you'll end up with words split on spaces (and anything else in $IFS
) instead of one word per array element.
使用bare ${array[@]}
or${array[*]}
将扩展的结果进行进一步的分词,因此您最终会在空格(以及 中的任何其他内容$IFS
)上拆分单词,而不是每个数组元素一个单词。
Using a C-style for loop is also fine and avoids worrying about word-splitting if you're not clear on it:
使用 C 风格的 for 循环也很好,如果你不清楚的话,可以避免担心分词:
for (( i = 0; i < ${#FILES[@]}; i++ ))
do
echo "${FILES[$i]}"
done
回答by Chris Seymour
Escaping works.
逃生工程。
#!/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]}
Output:
输出:
$ ./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
Quoting the strings also produces the same output.
引用字符串也会产生相同的输出。
回答by Jonni2016aa
If you had your array like this: #!/bin/bash
如果你的数组是这样的:#!/bin/bash
Unix[0]='Debian'
Unix[1]="Red Hat"
Unix[2]='Ubuntu'
Unix[3]='Suse'
for i in $(echo ${Unix[@]});
do echo $i;
done
You would get:
你会得到:
Debian
Red
Hat
Ubuntu
Suse
I don't know why but the loop breaks down the spaces and puts them as an individual item, even you surround it with quotes.
我不知道为什么,但循环分解了空格并将它们作为单独的项目放置,即使您用引号将其括起来。
To get around this, instead of calling the elements in the array, you call the indexes, which takes the full string thats wrapped in quotes. It must be wrapped in quotes!
为了解决这个问题,不是调用数组中的元素,而是调用索引,它采用用引号括起来的完整字符串。它必须用引号括起来!
#!/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
Then you'll get:
然后你会得到:
Debian
Red Hat
Ubuntu
Suse
回答by TNT
Not exactly an answer to the quoting/escaping problem of the original question but probably something that would actually have been more useful for the op:
不完全是原始问题的引用/转义问题的答案,但可能实际上对操作更有用:
unset FILES
for f in 2011-*.jpg; do FILES+=("$f"); done
echo "${FILES[@]}"
Where of course the expression would have to be adopted to the specific requirement (e.g. *.jpg
for all or 2001-09-11*.jpg
for only the pictures of a certain day).
当然,表达方式必须适用于特定要求(例如,*.jpg
针对所有或2001-09-11*.jpg
仅针对某一天的图片)。
回答by Javier Salas
Another solution is using a "while" loop instead a "for" loop:
另一种解决方案是使用“while”循环而不是“for”循环:
index=0
while [ ${index} -lt ${#Array[@]} ]
do
echo ${Array[${index}]}
index=$(( $index + 1 ))
done
回答by Mark Stosberg
If you aren't stuck on using bash
, different handling of spaces in file names is one of the benefits of the fish shell. Consider a directory which contains two files: "a b.txt" and "b c.txt". Here's a reasonable guess at processing a list of files generated from another command with bash
, but it fails due to spaces in file names you experienced:
如果您不坚持使用bash
,文件名中空格的不同处理是fish shell的好处之一。考虑一个包含两个文件的目录:“a b.txt”和“b c.txt”。这是处理从另一个命令生成的文件列表的合理猜测bash
,但由于您遇到的文件名中有空格而失败:
# bash
$ for f in $(ls *.txt); { echo $f; }
a
b.txt
b
c.txt
With fish
, the syntax is nearly identical, but the result is what you'd expect:
使用fish
,语法几乎相同,但结果是您所期望的:
# fish
for f in (ls *.txt); echo $f; end
a b.txt
b c.txt
It works differently because fish splits the output of commands on newlines, not spaces.
它的工作方式不同,因为 fish 在换行符而不是空格上拆分命令的输出。
If you have a case where you do want to split on spaces instead of newlines, fish
has a very readable syntax for that:
如果您确实想在空格而不是换行符上进行拆分,则fish
有一个非常易读的语法:
for f in (ls *.txt | string split " "); echo $f; end
回答by Madan Sapkota
I used to reset the IFSvalue and rollback when done.
我曾经重置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}
Possible output from the loop:
循环的可能输出:
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
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