Как назначить выражение glob переменной в Bash script?
Когда в bash script выполняются следующие две строки кода: "ls" жалуется, что файлы не существуют:
dirs=/content/{dev01,dev02}
ls -l $dirs
Когда я запускаю script с опцией -x, он, по-видимому, передает переменную в одинарные кавычки (что предотвращает глобус):
+ dirs=/content/{dev01,dev01}
+ ls -l '/content/{dev01,dev01}'
ls: /content/{dev01,dev01}: No such file or directory
Если я выполнил команду ls из моей интерактивной оболочки (без кавычек), она вернет два каталога.
Я читал справочное руководство bash (v 3.2) и не вижу никакой причины для того, чтобы globing filename не состоялся (я не передаю -f в оболочку) или что-либо, что я может быть настроен для обеспечения того, чтобы выполнялось глобирование.
Ответы
Ответ 1
Я думаю, что это порядок разложений:
Порядок разложений: brace
expansion
, расширение тильды, параметр, variable
и арифметическое расширение и подстановка команды (выполняется в слева направо), слово расщепление и pathname expansion
.
Итак, если ваша переменная заменена, расширение скобки больше не происходит. Это работает для меня:
eval ls $dirs
Будьте очень осторожны с eval. Он будет выполнять материал дословно. Поэтому, если dirs содержит f{m,k}t*; some_command
, команда some_ будет выполнена после завершения ls. Он выполнит строку, которую вы передаете eval
в текущей оболочке. Он пройдет /content/dev01 /content/dev02
до ls, независимо от того, существуют они или нет. Полагая *
после того, как материал делает его расширением pathname, и он будет пропускать несуществующие пути:
dirs=/content/{dev01,dev02}*
Я не уверен на 100%, но это имеет смысл для меня.
Ответ 2
Здесь - отличное обсуждение того, что вы пытаетесь сделать.
Короткий ответ: вам нужен массив:
dirs=(/content/{dev01,dev01})
Но то, что вы делаете с результатами, может усложниться, чем вы думаете.
Ответ 3
Это не переполнение имени файла, это расширение скобки.
Разница тонкая, но она существует - в подкачке файлов вы получаете только существующие файлы, в то время как в расширении брекета вы можете генерировать любую строку.
http://www.gnu.org/software/bash/manual/bashref.html#Brace-Expansion
http://www.gnu.org/software/bash/manual/bashref.html#Filename-Expansion
Теперь это то, что сработало для меня:
#!/bin/sh
dirs=`echo ./{dev01,dev02}`
ls $dirs
Ответ 4
Я подозреваю, что вам нужен массив, но это ограничит вас новыми слоями. Это заставка, чем использование eval.
dirs=( /"content with spaces"/{dev01,dev02} )
dirs=( /content/{dev01,dev02} )
ls -l "${dirs[@]}"
/content/{dev01,dev02}
будет расширяться до:
"/content/dev01" "/content/dev02"
Существование этих каталогов не имеет отношения к разложению.
Это становится непредсказуемым, если вы назначаете переменную расширению скобки.
dirs=/content/{dev01,dev02}
может превратиться в
"/content/dev01"
или
"/content/dev01 /content/dev02"
или
"/content/dev01" "/content/dev02"
или
"/content/{dev01,dev02}"
Если вы произвольно цитируете фигурные скобки, они не будут расширяться, поэтому результат будет содержать фигурные скобки и будет в основном бессмысленным.
Ответ 5
Поскольку вы хотите glob файлы, вам не следует использовать расширения расширений. Использование расширения скобок в этом случае является антипаттерном и, безусловно, неправильным инструментом для работы.
Что вам нужно, расширенное подталкивание:
shopt -s extglob # likely already set in interactive shells
dirs=/content/@(dev01|dev02)
ls $dirs
Ответ 6
Для людей (таких как я), обнаруживающих это через Google, ответы @Peter и @feoh являются общим решением для "Как объединить переменные в bash script".
list_of_results=(pattern)
сохранит существующие имена файлов, соответствующие pattern
, в массив list_of_results
. Каждый элемент list_of_results
будет содержать одно имя файла, пробелы и все.
Вы можете получить доступ к каждому результату как "${list_of_results[<index>]}"
для <index>
начиная с 0. Вы можете получить весь список, правильно процитированный, как "${list_of_results[@]}"
.
Ответ 7
ls `echo $dirs`
работает под cygwin.