元素中包含空格的 Bash 数组
我正在尝试在 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)
如您所见,每个文件名中间有一个空格。
我尝试过将每个名称用引号引起来,并用反斜杠转义空格,但这两种方法都不起作用。
当我尝试访问数组元素时,它继续将空格视为元素分隔符。
如何正确捕获名称中带有空格的文件名?
I'm trying to construct an array in bash of the filenames from my camera:
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 element delimiter.
How can I properly capture the filenames with a space inside the name?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
我认为问题可能部分在于您访问元素的方式。如果我对 $FILES 中的 elem 执行一个简单的
for elem
,我会遇到与您相同的问题。但是,如果我通过数组的索引访问数组,就像这样,如果我以数字方式或使用转义符添加元素,它就会起作用:$FILES
的任何这些声明都应该起作用:或
或
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:Any of these declarations of
$FILES
should work:or
or
您访问数组项的方式一定有问题。其实现方式如下:
来自 bash 手册页:
当然,访问单个成员时也应该使用双引号
There must be something wrong with the way you access the array's items. Here's how it's done:
From the bash manpage:
Of course, you should also use double quotes when accessing a single member
您需要使用 IFS 来停止空格作为元素分隔符。
如果你想基于 .然后只需执行 IFS="."
希望对你有帮助:)
You need to use IFS to stop space as element delimiter.
If you want to separate on basis of . then just do IFS="."
Hope it helps you:)
我同意其他人的观点,即问题可能在于您访问元素的方式。在数组赋值中引用文件名是正确的:
在
"${FILES[@]}"
形式的任何数组周围使用双引号会将数组拆分为每个数组元素一个单词。除此之外它不会进行任何分词。使用
"${FILES[*]}"
也有特殊含义,但它将数组元素与 $IFS 的第一个字符连接起来,导致 一个 这个词,这可能不是你想要的。使用裸露的
${array[@]}
或${array[*]}
会使扩展的结果受到进一步的分词,所以你最终会得到用空格(以及$IFS
中的其他任何内容)分割单词,而不是每个数组元素一个单词。使用 C 风格的 for 循环也很好,并且可以避免在您不清楚的情况下担心分词:
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:
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.Using
"${FILES[*]}"
also has a special meaning, but it joins the array elements with the first character of $IFS, resulting in one word, which is probably not what you want.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.Using a C-style for loop is also fine and avoids worrying about word-splitting if you're not clear on it:
上面已经回答了这个问题,但是这个答案有点简洁,手册页摘录也有点神秘。我想提供一个完整的示例来演示这在实践中是如何工作的。
如果不加引号,数组只会扩展为由空格分隔的字符串,因此
扩展为
但如果引用扩展,bash 在每个术语周围添加双引号,以便:
扩展为
简单的经验法则是始终使用
如果您想保留空格,请使用 [@]
而不是[*]
并引用数组扩展。为了进一步详细说明这一点,另一个答案中的手册页解释说,如果不加引号,
$*
和$@
的行为方式相同,但当它们不同时引。因此,给定Then
$*
和$@
都扩展为且
"$*"
扩展为"$@" 扩展为
This was already answered above, but that answer was a bit terse and the man page excerpt is a bit cryptic. I wanted to provide a fully worked example to demonstrate how this works in practice.
If not quoted, an array just expands to strings separated by spaces, so that
expands to
But if you quote the expansion, bash adds double quotes around each term, so that:
expands to
The simple rule of thumb is to always use
[@]
instead of[*]
and quote array expansions if you want spaces preserved.To elaborate on this a little further, the man page in the other answer is explaining that if unquoted,
$*
an$@
behave the same way, but they are different when quoted. So, givenThen
$*
and$@
both expand toand
"$*"
expands toand
"$@"
expands to如果你的数组是这样的:
#!/bin/bash
你会得到:
我不知道为什么,但循环会分解空格并将它们作为单独的项目,即使你用引号将其引起来。
为了解决这个问题,您不调用数组中的元素,而是调用索引,它采用引号括起来的完整字符串。
它必须用引号引起来!
然后你会得到:
If you had your array like this:
#!/bin/bash
You would get:
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!
Then you'll get:
对于那些喜欢在单行模式下设置数组的人,不要使用 for 循环,
暂时将
IFS
更改为新行可以避免您转义。For those who prefer set array in oneline mode, instead of using for loop
Changing
IFS
temporarily to new line could save you from escaping.不完全是原始问题的引用/转义问题的答案,但可能实际上对操作更有用:
当然,必须采用表达式来满足特定要求(例如
*.jpg
表示所有图片,或2001-09-11*.jpg
表示仅某一天的图片)。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:
Where of course the expression would have to be adopted to the specific requirement (e.g.
*.jpg
for all or2001-09-11*.jpg
for only the pictures of a certain day).输出
`
OUTPUT
`
逃跑有效。
输出:
引用字符串也会产生相同的输出。
Escaping works.
Output:
Quoting the strings also produces the same output.
如果 FILES 的元素来自另一个文件,其文件名像这样以行分隔:
然后尝试此操作,以便文件名中的空格不被视为分隔符:
如果它们来自另一个命令,您需要像这样重写最后一行:
If the elements of
FILES
come from another file whose file names are line-separated like this:then try this so that the whitespaces in the file names aren't regarded as delimiters:
If they come from another command, you need to rewrite the last line like this:
另一个解决方案是使用“while”循环而不是“for”循环:
Another solution is using a "while" loop instead a "for" loop:
如果您不坚持使用
bash
,对文件名中的空格进行不同处理是 鱼壳。考虑一个包含两个文件的目录:“a b.txt”和“b c.txt”。这是使用bash
处理另一个命令生成的文件列表的合理猜测,但由于您遇到的文件名中的空格而失败:使用
fish
,语法几乎是相同,但结果正如您所期望的:它的工作方式不同,因为 Fish 在换行符上分割命令的输出,而不是空格。
如果您确实想按空格而不是换行符进行分割,则
fish
有一个非常可读的语法: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 withbash
, but it fails due to spaces in file names you experienced:With
fish
, the syntax is nearly identical, but the result is what you'd expect:It works differently because fish splits the output of commands on newlines, not spaces.
If you have a case where you do want to split on spaces instead of newlines,
fish
has a very readable syntax for that:我曾经重置 IFS 值并在完成后回滚。
循环的可能输出:
I used to reset the IFS value and rollback when done.
Possible output from the loop: