Системная информация
ОС: OS X
bash: GNU bash, версия 3.2.57 (1) -релиз (x86_64-apple-darwin16)
Фон
Я хочу, чтобы машина времени исключала набор каталогов и файлов из всего моего проекта git / nodejs. Мои каталоги проектов находятся в, ~/code/private/
и ~/code/public/
поэтому я пытаюсь использовать цикл bash, чтобы сделать tmutil
.
вопрос
Укороченная версия
Если у меня есть вычисляемая строковая переменная k
, как мне сделать ее внутри или прямо перед циклом for:
i='~/code/public/*'
j='*.launch'
k=$i/$j # $k='~/code/public/*/*.launch'
for i in $k # I need $k to glob here
do
echo $i
done
В длинной версии ниже вы увидите k=$i/$j
. Поэтому я не могу жестко закодировать строку в цикле for.
Длинная версия
#!/bin/bash
exclude='
*.launch
.classpath
.sass-cache
Thumbs.db
bower_components
build
connect.lock
coverage
dist
e2e/*.js
e2e/*.map
libpeerconnection.log
node_modules
npm-debug.log
testem.log
tmp
typings
'
dirs='
~/code/private/*
~/code/public/*
'
for i in $dirs
do
for j in $exclude
do
k=$i/$j # It is correct up to this line
for l in $k # I need it glob here
do
echo $l
# Command I want to execute
# tmutil addexclusion $l
done
done
done
Выход
Они не сгущены. Не то, что я хочу.
~/code/private/*/*.launch
~/code/private/*/.DS_Store
~/code/private/*/.classpath
~/code/private/*/.sass-cache
~/code/private/*/.settings
~/code/private/*/Thumbs.db
~/code/private/*/bower_components
~/code/private/*/build
~/code/private/*/connect.lock
~/code/private/*/coverage
~/code/private/*/dist
~/code/private/*/e2e/*.js
~/code/private/*/e2e/*.map
~/code/private/*/libpeerconnection.log
~/code/private/*/node_modules
~/code/private/*/npm-debug.log
~/code/private/*/testem.log
~/code/private/*/tmp
~/code/private/*/typings
~/code/public/*/*.launch
~/code/public/*/.DS_Store
~/code/public/*/.classpath
~/code/public/*/.sass-cache
~/code/public/*/.settings
~/code/public/*/Thumbs.db
~/code/public/*/bower_components
~/code/public/*/build
~/code/public/*/connect.lock
~/code/public/*/coverage
~/code/public/*/dist
~/code/public/*/e2e/*.js
~/code/public/*/e2e/*.map
~/code/public/*/libpeerconnection.log
~/code/public/*/node_modules
~/code/public/*/npm-debug.log
~/code/public/*/testem.log
~/code/public/*/tmp
~/code/public/*/typings
bash
shell-script
quoting
wildcards
Джон Сиу
источник
источник
k
является вычисляемой строкой, и мне нужно, чтобы она оставалась такой до цикла. Пожалуйста, проверьте мою длинную версию.Ответы:
Вы можете провести еще один раунд оценки
eval
, но на самом деле это не обязательно. (Иeval
начинаются серьезные проблемы, когда имена ваших файлов содержат специальные символы, такие как$
.) Проблема не в глобализации, а в расширении тильды.Глобирование происходит после раскрытия переменной, если переменная не заключена в кавычки, как здесь (*) :
Итак, в том же духе, это похоже на то, что вы сделали, и работает:
Но с тильдой это не так:
Это ясно задокументировано для Bash:
Расширение тильды происходит до раскрытия переменных, поэтому тильды внутри переменных не раскрываются. Простой обходной путь - использовать
$HOME
полный путь или вместо него.(* расширение глобусов из переменных обычно не то, что вы хотите)
Еще одна вещь:
Когда вы перебираете шаблоны, как здесь:
обратите внимание, что, как
$exclude
и в кавычках, он и расколот, и в этом месте также слит. Таким образом, если текущий каталог содержит что-то, соответствующее шаблону, он расширяется до следующего:Чтобы обойти это, используйте переменную-массив вместо разделенной строки:
В качестве дополнительного бонуса записи массива также могут содержать пробелы без проблем с разбиением.
Нечто подобное можно сделать
find -path
, если вы не возражаете против того, каким уровнем каталогов должны быть целевые файлы. Например, чтобы найти любой путь, заканчивающийся на/e2e/*.js
:Мы должны использовать
$HOME
вместо того же~
по той же причине, что и раньше, и$dirs
должны быть заключены в кавычки вfind
командной строке, чтобы разделить их, но$pattern
должны быть заключены в кавычки, чтобы случайно не раскрыться оболочкой.(Я думаю, что вы могли бы поиграть с
-maxdepth
GNU find, чтобы ограничить глубину поиска, если вам интересно, но это немного другая проблема.)источник
find
? На самом деле я тоже изучаю этот маршрут, так как цикл for усложняется. Но у меня возникли проблемы с «-path»."${array[@]}"
(с кавычками!) задокументировано (см. здесь и здесь ) расширить элементы как отдельные слова, не разбивая их дальше.[abc]
это стандартная часть шаблонов глобусов , вроде бы?
, я не думаю, что здесь необходимо охватить все из них.Вы можете сохранить его в виде массива вместо строки, чтобы использовать его позже во многих случаях и разрешить глобализацию при ее определении. В вашем случае, например:
или в последнем примере вам понадобятся
eval
некоторые строкиисточник
$exclude
содержит подстановочные знаки, вам нужно отключить глобирование перед использованием оператора split + glob на нем и восстановить его для использования$i/$j
и не использовать,eval
а использовать"$i"/$j
Ответ @ilkkachu решил основную проблему. Полный кредит ему.
V1
Однако из-за того, что в них
exclude
содержатся записи как с подстановочными знаками (*), так и без них, а также они могут отсутствовать во всех, требуется дополнительная проверка после выделения$i/$j
. Я делюсь своими выводами здесь.Объяснение вывода
Ниже приводится частичный вывод, чтобы объяснить ситуацию.
Вышесказанное говорит само за себя.
Выше показано, потому что exclude entry (
$j
) не имеет подстановочных знаков,$i/$j
становится простой конкатенацией строк. Однако файл / dir не существует.Выше показано, что как exclude entry (
$j
) содержит подстановочный знак, но не соответствует файлу / каталогу,$i/$j
просто возвращает исходную строку.V2
V2 использовать одинарные кавычки
eval
иshopt -s nullglob
получить чистый результат. Не требуется проверка файла / каталога.источник
for j in $exclude
, что$exclude
во время этого$exclude
расширения глобусы могут быть расширены (и вызовeval
этого порождает проблемы). Вы бы хотели, чтобы глобирование было включеноfor i in $dir
,for l in $k
но не дляfor j in $exclude
. Вы бы хотелиset -f
до последнего иset +f
для другого. В более общем смысле, вы бы хотели настроить свой оператор split + glob перед его использованием. В любом случае, вы не хотите, чтобы split + glob использовалсяecho $l
, поэтому$l
должны быть указаны там.exclude
иdirs
находятся в одинарных), so no globbing till
кавычках ( eval`.foo=*
иfoo='*'
то же самое. Ноecho $foo
иecho "$foo"
нет (в оболочках типаbash
, это было исправлено в оболочках, таких как zsh, fish или rc, см. Также ссылку выше). Здесь же хочу использовать этот оператор, но в некоторых местах только разделительную часть, а в других только Глоба части.С
zsh
:${^array}string
это расширить как$array[1]string $array[2]string...
.$=var
выполняет разделение слов в переменной (что по умолчанию делают другие оболочки!),$~var
выполняет глобализацию переменной (что по умолчанию делают и другие оболочки (когда вы, как правило, не хотите этого, вы должны были бы процитировать$f
выше в другие снаряды)).(N)
является классификатором глобуса, который включает nullglob для каждого из этих глобусов, полученных в результате этого$^array1/$^array2
расширения. Это заставляет шары расширяться до нуля, когда они не совпадают. Это также происходит, чтобы превратить не-глобус, как~/code/private/foo/Thumbs.db
в один, что означает, что если этот конкретный не существует, он не включается.источник
exclude
заключается в том, что влияет на вывод.$^array
должны быть выполнены в два отдельных шага, чтобы убедиться, что пустые элементы отбрасываются (см. Редактирование). Это похоже на ошибкуzsh
, я подниму вопрос в их списке рассылки.