Как назначить ls массиву в Linux Bash?

Ответы:

111

Было бы это

array=($(ls -d */))

РЕДАКТИРОВАТЬ: см. Решение Гордона Дэвиссона для более общего ответа (например, если ваши имена файлов содержат специальные символы). Этот ответ - просто исправление синтаксиса.

Аарон Окано
источник
14
Это не будет работать правильно, если имена каталогов содержат пробелы.
chepner
1
Чтобы решить проблему пробелов или специальных символов, сначала объявите массив без каких-либо значений, затем используйте встроенную ARR+=('$(ls -d */)')функцию для редактирования элементов в массиве, используя одинарные кавычки для работы со специальными символами, чтобы добавить или удалить элементы в него. массив. Одиночные кавычки сохраняют буквальные символы и устраняют необходимость в экранировании, чтобы иметь дело с ними в именах каталогов.
Yokai
@Yokai Ваше предложение звучит замечательно, но у меня не сработало на Ubuntu 16.04 или MacOs. Bash расширяет код между одинарными кавычками следующим образом: declare -a ARR; ARR+=('$(ls -d "$INPUT_DIR"/*)')возвращает только$(ls -d $INPUT_DIR/*)
Agile Bean
1
Моя ошибка. Это должны были быть двойные кавычки, чтобы можно было расширить подстановку команд. ARR+=("$(ls -d */)")должно сработать. Если не просто дайте мне знать.
Yokai
2
@Yokai, который добавляет только один элемент (с кучей новых строк в нем), а не один элемент для каждого каталога.
Чарльз Даффи
80

По возможности следует избегать синтаксического анализа вывода ls(см . Вики Грега по этой теме ). Обычно вывод lsбудет неоднозначным, если в любом из имен файлов есть забавные символы. Также обычно это пустая трата времени. В этом случае, когда вы выполняете ls -d */, происходит то, что оболочка расширяется */до списка подкаталогов (который уже является именно тем, что вам нужно), передает этот список в качестве аргументов в ls -d, который просматривает каждый из них и говорит: «Да, это каталог. хорошо »и распечатывает его (в противоречивом, а иногда и неоднозначном формате). Команда lsне делает ничего полезного!

Хорошо, хорошо, он делает одну полезную вещь: если нет подкаталогов, */он останется как есть, lsбудет искать подкаталог с именем «*», но не находить его, печатать сообщение об ошибке, что он не существует (чтобы stderr), а не выводить «* /» (на стандартный вывод).

Более простой способ создать массив имен подкаталогов - использовать glob ( */), не передавая его в ls. Но чтобы избежать добавления "* /" в массив, если фактических подкаталогов нет, вы должны сначала установить nullglob (опять же, см . Вики Грега ):

shopt -s nullglob
array=(*/)
shopt -u nullglob # Turn off nullglob to make sure it doesn't interfere with anything later
echo "${array[@]}"  # Note double-quotes to avoid extra parsing of funny characters in filenames

Если вы хотите распечатать сообщение об ошибке при отсутствии подкаталогов, вам лучше сделать это самостоятельно:

if (( ${#array[@]} == 0 )); then
    echo "No subdirectories found" >&2
fi
Гордон Дэвиссон
источник
2
Спасибо за ваш комментарий, хотя он и не отмечен как ответ, он был достаточно информативным, и я ценю его.
Jake H
Что делать, если вы не хотите /использовать имя каталога?
shinzou
Проблемы / проблемы, описанные в вики-странице greg, не могут быть легко решены с помощью простых условных конструкций. lsэто простая команда. Его вывод - это строки. Bash очень хорошо разбирается в строках. Правильное регулярное выражение - это все, что необходимо для безопасного анализа lsвывода.
Yokai
@Yokai Различные версии lsделают разные вещи с забавными / непечатаемыми символами в именах файлов, поэтому нет единого способа преобразовать его вывод в список имен файлов. И, как я уже сказал, на самом деле он не делает ничего полезного: если вы правильно проанализируете выводls -d */ , вы получите то же самое, что и получили бы напрямую */(кроме случаев, когда совпадений нет, и это легко проверить).
Гордон Дэвиссон
Итак, чтобы подвести итог: A lazy sysadmin is a good sysadmin. возможно, мой собственный lsсинтаксический анализ специфичен для той версии, которая у меня есть или к которой я привык, но пока у них не было проблем с дистрибутивами Linux, на которых я их тестировал. Я могу говорить только по опыту.
Ёкай
8

Это будет печатать файлы в этих каталогах построчно.

array=(ww/* ee/* qq/*)
printf "%s\n" "${array[@]}"
консольбокс
источник