Я пытаюсь развернуть строку, включающую подстановочный знак и набор расширений, указанных в фигурных скобках. Кажется, ничего не работает, как показано в примере ниже. переменная firstList
разворачивается нормально, но ни secondList
, thirdList
или fourthList
разворачивается правильно. Я также пробовал различные версии , eval
но ни одна не работает. Любая помощь будет оценена по достоинству
#!/bin/bash
touch a.ext1
touch b.ext1
firstList='*.ext1'
ls $firstList
touch a.ext2
touch b.ext2
secondList='*.{ext1,ext2}'
ls $secondList
ls '$secondList'
ls "$secondList"
thirdList=*.{ext1,ext2}
ls $thirdList
ls '$thirdList'
ls "$thirdList"
fourthList="*.{ext1,ext2}"
ls $fourthList
ls '$fourthList'
ls "$fourthList"
решение1
Theоболочкарасширяется *
только если не заключено в кавычки, любое кавычки останавливает расширение оболочкой.
Кроме того, для того чтобы расширение скобок было развернуто оболочкой, его необходимо раскавычить.
Это работает (давайте используем echo, чтобы увидеть, что делает оболочка):
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
Даже если есть файлы с другими именами:
$ touch {a,b}.{ext1,ext2} {c,d}.{ext3,ext4} none
ls
a.ext1 a.ext2 b.ext1 b.ext2 c.ext3 c.ext4 d.ext3 d.ext4 none
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
Почему это работает?
Важно, чтобы мы понимали, почему это работает. Это из-за порядка расширения. Сначала «Расширение скобок», а затем (последнее) «Расширение имени пути» (также известное как расширение глобуса).
Brace --> Parameter (variable) --> Pathname
Мы можем на время отключить «Расширение имени пути»:
$ set -f
$ echo *.{ext1,ext2}
*.ext1 *.ext2
«Расширение имени пути» принимает два аргумента: *.ext1
и *.ext2
.
$ set +f
$ echo *.{ext1,ext2}
a.ext1 b.ext1 a.ext2 b.ext2
Проблема в том, что мы не можем использовать переменную для расширения скобок.
Это было объяснено много раз ранее дляиспользование переменной внутри «Расширения фигурных скобок»
Чтобы развернуть «Расширение фигурных скобок», которое является результатом «Расширения переменной», вам необходимо повторно отправить командную строку в оболочку с помощью eval
.
$ list={ext1,ext2}
$ eval echo '*.'"$list"
Скобка -->Переменная--> Глоб || -->Подтяжка--> Переменная -->Глоб
........ цитируется здесь -->^^^^^^ || оценка ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Значения имен файлов не вызывают проблем с выполнением eval:
$ touch 'a;date;.ext1'
eval echo '*.'"$list"
a;date;.ext1 a.ext1 b.ext1 a.ext2 b.ext2
Но значение $list
может быть небезопасным. Однако значение $list
задается сценаристом. Сценарист контролирует eval
: Просто не используйте внешние заданные значения для $list
. Попробуйте это:
#!/bin/bash
touch {a,b,c}.ext{1,2}
list=ext{1,2}
eval ls -l -- '*.'"$list"
Лучшая альтернатива.
Альтернативой (без eval) являетсяиспользовать Bash «Расширенные шаблоны»:
#!/bin/bash
shopt -s extglob
list='@(ext1|ext2)'
ls -- *.$list
Примечание: Имейте в виду, что оба решения (eval и patterns) (как написано) безопасны для имен файлов с пробелами или новыми строками. Но не сработают для с $list
пробелами, поскольку $list
не заключен в кавычки или eval удаляет кавычки.
решение2
Учитывать:
secondList='*.{ext1,ext2}'
ls $secondList
Проблема в том, чторасширение скобкиготоводо переменное расширениеЭто означает, что в приведенном выше примере расширение скобок никогда не выполняется.
Это потому, что когда bash впервые видит командную строку, там нет фигурных скобок. После того secondList
, как она развернута, уже слишком поздно.
Будет работать следующее:
$ s='*'
$ ls $s.{ext1,ext2}
a.ext1 a.ext2 b.ext1 b.ext2
Здесь в командной строке есть фигурные скобки, поэтомурасширение скобкиможет быть выполнено в качестве первого шага. После этого значение $s
подставляется в (переменное расширение), И наконецрасширение имени путивыполняется.
Документация
man bash
объясняет порядок расширения:
Порядок расширений следующий: расширение фигурных скобок; расширение тильды, расширение параметров и переменных, арифметическое расширение и подстановка команд (выполняется слева направо); разделение слов; и расширение имени пути.