Bash массив с пробелами в элементах - PullRequest
125 голосов
/ 31 января 2012

Я пытаюсь создать в 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)

Как видите, в середине каждого имени файла есть пробел.

Я пытался заключить каждое имя в кавычки и экранировать пробел обратной косой чертой, ни один из которых не работает.

Когда я пытаюсь получить доступ к элементам массива, он продолжает обрабатывать пространство как ограничитель элементов.

Как правильно записать имена файлов с пробелом внутри имени?

Ответы [ 9 ]

102 голосов
/ 31 января 2012

Я думаю, что проблема может быть отчасти связана с тем, как вы получаете доступ к элементам. Если я делаю простой for elem in $FILES, я испытываю ту же проблему, что и вы. Однако, если я получу доступ к массиву через его индексы, он будет работать, если я добавлю элементы численно или с помощью escape-кодов:

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"
82 голосов
/ 01 февраля 2012

Должно быть, что-то не так с доступом к элементам массива.Вот как это делается:

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

Из справочной страницы bash :

На любой элемент массива можно ссылаться с помощью $ {name [subscript]},... Если индекс равен @ или *, слово распространяется на всех членов имени.Эти индексы отличаются только тогда, когда слово появляется в двойных кавычках. Если слово заключено в двойные кавычки, $ {name [*]} раскрывается в одно слово со значением каждого члена массива, разделенного первым символом специальной переменной IFS, и $ {name [@]} расширяет каждый элемент имени до отдельного слова .

Конечно, вы также должны использовать двойные кавычки при доступе к одному члену

cp "${files[0]}" /tmp
37 голосов
/ 24 сентября 2012

Вам нужно использовать 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 = "." Надеюсь, это поможет вам:)

11 голосов
/ 09 мая 2013

Я согласен с другими, что, скорее всего, как вы получаете доступ к элементам, это проблема.Правильно указывать имена файлов в назначении массива:

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) вместо одного слова на элемент массива.

Использование цикла for в стиле C также прекрасно и позволяет избежать беспокойства о разбиении слов, если вынеясно на этом:

for (( i = 0; i < ${#FILES[@]}; i++ ))
do
  echo "${FILES[$i]}"
done
2 голосов
/ 02 августа 2017

Не совсем ответ на проблему цитирования / экранирования исходного вопроса, но, вероятно, что-то, что на самом деле было бы более полезным для операции:

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

Где, конечно, должно быть принято выражениек конкретному требованию (например, *.jpg для всех или 2001-09-11*.jpg только для фотографий определенного дня).

2 голосов
/ 31 января 2012

Спасательные работы.

#!/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

Заключение в кавычки также приводит к тому же выводу.

1 голос
/ 06 мая 2019

Если бы у вас был такой массив: #! / Bin / Баш

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
0 голосов
/ 13 июня 2019

Если вы не застряли на использовании 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
0 голосов
/ 16 октября 2017

Другое решение - использовать цикл while вместо цикла for:

index=0
while [ ${index} -lt ${#Array[@]} ]
  do
     echo ${Array[${index}]}
     index=$(( $index + 1 ))
  done
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...