Informação do sistema
SO: OS X
bash: GNU bash, versão 3.2.57 (1) -release (x86_64-apple-darwin16)
fundo
Quero que o time machine exclua um conjunto de diretórios e arquivos de todo o meu projeto git / nodejs. Meus diretórios de projeto estão ~/code/private/
e ~/code/public/
por isso estou tentando usar o loop bash para fazer o tmutil
.
Questão
Versão curta
Se eu tenho uma variável de string calculadak
, como faço para torná-la global antes ou antes de um loop 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
Na versão longa abaixo, você verá k=$i/$j
. Portanto, não posso codificar a string no loop for.
Versão longa
#!/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
Resultado
Eles não são globbed. Não é o que eu quero.
~/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
John Siu
fonte
fonte
k
é uma string calculada e preciso que continue assim até o loop. Por favor, verifique minha versão longa.Respostas:
Você pode forçar outra rodada de avaliação
eval
, mas isso não é realmente necessário. (Eeval
começa a ter problemas sérios no momento em que os nomes de seus arquivos contêm caracteres especiais como$
.) O problema não é com globbing, mas com a expansão do til.O globbing ocorre após a expansão da variável, se a variável não estiver entre aspas, como aqui (*) :
Portanto, na mesma linha, isso é semelhante ao que você fez e funciona:
Mas com o til não:
Isso está claramente documentado para o Bash:
A expansão do til ocorre antes da expansão da variável, portanto, os tildes dentro das variáveis não são expandidos. A solução fácil é usar
$HOME
ou o caminho completo.(* expansão de globs de variáveis geralmente não é o que você deseja)
Outra coisa:
Quando você faz um loop sobre os padrões, como aqui:
observe que, como
$exclude
não é citado, ele é dividido e também está cheio neste momento. Portanto, se o diretório atual contiver algo que corresponda ao padrão, ele será expandido para isso:Para contornar isso, use uma variável de matriz em vez de uma seqüência de caracteres dividida:
Como um bônus adicional, as entradas da matriz também podem conter espaços em branco sem problemas com a divisão.
Algo semelhante poderia ser feito
find -path
, se você não se importar com o nível de diretório dos arquivos de destino. Por exemplo, para encontrar qualquer caminho que termine em/e2e/*.js
:Temos que usar
$HOME
em vez de~
pela mesma razão como antes, e$dirs
precisa ser não cotadas nafind
linha de comando por isso fica dividida, mas$pattern
deve ser citado por isso não é acidentalmente expandida pelo shell.(Acho que você pode brincar com o
-maxdepth
GNU para limitar a profundidade da pesquisa, se você se importa, mas isso é um pouco diferente.)fonte
find
? Na verdade, também estou explorando essa rota, pois o loop for está ficando complicado. Mas estou tendo dificuldades com o '-path'."${array[@]}"
(com as aspas!) está documentada (veja aqui e aqui ) para expandir para os elementos como palavras distintas sem dividi-las ainda mais.[abc]
é uma parte padrão dos padrões glob , tipo?
, eu não acho que seja necessário cobrir todos eles aqui.Você pode salvá-lo como uma matriz, em vez de uma string, para usá-lo posteriormente em muitos casos, e permitir que o globbing aconteça quando você o definir. No seu caso, por exemplo:
ou no exemplo posterior, você precisará de
eval
algumas das stringsfonte
$exclude
contém wildcards, você precisa desativar englobamento antes de utilizar a divisão + glob operador nele e restaurá-lo para o$i/$j
e não usareval
, mas o uso"$i"/$j
A resposta do @ilkkachu resolveu o principal problema de globbing. Crédito total para ele.
V1
No entanto, devido ao fato de
exclude
conter entradas com e sem curinga (*), e elas também podem não existir, é necessária uma verificação extra após o globbing de$i/$j
. Estou compartilhando minhas descobertas aqui.Explicação da saída
A seguir, é apresentada a saída parcial para explicar a situação.
Os itens acima são auto-explicativos.
O exemplo acima é exibido porque a entrada de exclusão (
$j
) não possui curinga,$i/$j
torna-se uma concatenação de sequência simples. No entanto, o arquivo / dir não existe.Os itens acima são exibidos como entrada de exclusão (
$j
) contêm curinga, mas não têm correspondência de arquivo / diretório; o globbing$i/$j
apenas retorna a string original.V2
V2 use aspas simples
eval
eshopt -s nullglob
obtenha um resultado limpo. Nenhuma verificação final de arquivo / diretório requer.fonte
for j in $exclude
, no caso , os globs$exclude
podem ser expandidos no momento dessa$exclude
expansão (e solicitareval
isso está causando problemas). Você gostaria que o globbing estivesse ativado parafor i in $dir
, efor l in $k
não parafor j in $exclude
. Você gostaria de umset -f
antes do último e umset +f
para o outro. Em geral, você deseja ajustar seu operador split + glob antes de usá-lo. De qualquer forma, você não deseja split + glob paraecho $l
, portanto,$l
deve ser citado lá.exclude
edirs
estão entre aspas simples (), so no globbing till
eval`.foo=*
efoo='*'
é o mesmo. Masecho $foo
eecho "$foo"
não é (em conchas comobash
, foi fixado em conchas como zsh, fish ou rc, veja também o link acima). Aqui você não quiser usar esse operador, mas em alguns lugares apenas a parte dividida, e em outros apenas a parte glob.Com
zsh
:${^array}string
é expandir como$array[1]string $array[2]string...
.$=var
é executar a divisão de palavras na variável (algo que outras conchas fazem por padrão!),$~var
faz globbing na variável (algo que outras conchas também por padrão (quando você geralmente não as deseja, você teria que citar$f
acima) outras conchas)).(N)
é um qualificador glob que ativa o nullglob para cada um desses globs resultantes dessa$^array1/$^array2
expansão. Isso faz com que os globs se expandam para nada quando não combinam. Isso também transforma um não-glob como~/code/private/foo/Thumbs.db
em um, o que significa que, se esse particular não existir, ele não será incluído.fonte
exclude
está afetando a saída.$^array
deve ser feito em duas etapas separadas para garantir que os elementos vazios sejam descartados (consulte a edição). Isso parece um bugzsh
, vou abordar a questão na lista de discussão deles.