元素中带有空格的 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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 21:36:30  来源:igfitidea点击:

Bash array with spaces in elements

arraysbashunixscripting

提问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 $FILESshould 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:

bash 联机帮助页

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. *.jpgfor all or 2001-09-11*.jpgfor 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, fishhas 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