Information système
OS: OS X
bash: GNU bash, version 3.2.57 (1) -release (x86_64-apple-darwin16)
Contexte
Je veux que Time Machine exclue un ensemble de répertoires et de fichiers de tout mon projet git / nodejs. Mes répertoires de projet sont dedans ~/code/private/
et ~/code/public/
j'essaye donc d'utiliser la boucle bash pour faire le tmutil
.
Problème
Version courte
Si j'ai une variable de chaîne calculéek
, comment puis-je la faire glob dans ou juste avant une boucle 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
Dans la version longue ci-dessous, vous verrez k=$i/$j
. Je ne peux donc pas coder en dur la chaîne dans la boucle for.
Version longue
#!/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
Production
Ils ne sont pas globulés. Pas ce que je veux.
~/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
la source
la source
k
est une chaîne calculée, et j'en ai besoin de rester ainsi jusqu'à la boucle. Veuillez vérifier ma version longue.Réponses:
Vous pouvez forcer un autre cycle d'évaluation avec
eval
, mais ce n'est pas réellement nécessaire. (Eteval
commence à avoir de sérieux problèmes au moment où vos noms de fichiers contiennent des caractères spéciaux comme$
.) Le problème n'est pas avec la globalisation, mais avec l'extension tilde.Le globalisation se produit après l' expansion de la variable, si la variable n'est pas citée, comme ici (*) :
Donc, dans la même veine, cela ressemble à ce que vous avez fait et fonctionne:
Mais avec le tilde, ce n'est pas le cas:
Ceci est clairement documenté pour Bash:
L'expansion du tilde se produit avant l'expansion des variables, donc les tildes à l'intérieur des variables ne sont pas développés. La solution de contournement simple consiste à utiliser
$HOME
ou le chemin complet à la place.(* l'expansion des globes à partir des variables n'est généralement pas ce que vous voulez)
Autre chose:
Lorsque vous parcourez les motifs, comme ici:
notez que, comme il
$exclude
n'est pas cité, il est à la fois divisé et également globulé à ce stade. Donc, si le répertoire actuel contient quelque chose qui correspond au modèle, il est étendu à cela:Pour contourner ce problème, utilisez une variable de tableau au lieu d'une chaîne fractionnée:
En prime, les entrées de tableau peuvent également contenir des espaces sans problème de division.
Quelque chose de similaire pourrait être fait avec
find -path
, si cela ne vous dérange pas quel niveau de répertoire les fichiers ciblés devraient être. Par exemple, pour trouver un chemin se terminant par/e2e/*.js
:Nous devons utiliser
$HOME
au lieu de~
pour la même raison que précédemment, et$dirs
doit être non cité sur lafind
ligne de commande pour qu'il soit divisé, mais$pattern
doit être cité afin qu'il ne soit pas accidentellement développé par le shell.(Je pense que vous pouvez jouer avec
-maxdepth
GNU find pour limiter la profondeur de la recherche, si cela vous intéresse, mais c'est un peu un problème différent.)la source
find
? En fait, j'explore également cette route, car la boucle for se complique. Mais j'ai du mal avec le '-path'."${array[@]}"
(avec les guillemets!) est documenté (voir ici et ici ) pour s'étendre aux éléments en tant que mots distincts sans les diviser davantage.[abc]
est une partie standard des modèles glob , comme?
, je ne pense pas qu'il soit nécessaire d'aller les couvrir tous ici.Vous pouvez l'enregistrer en tant que tableau au lieu d'une chaîne pour l'utiliser ultérieurement dans de nombreux cas et laisser la globalisation se produire lorsque vous la définissez. Dans votre cas, par exemple:
ou dans l'exemple suivant, vous aurez besoin de
eval
certaines des chaînesla source
$exclude
contient les caractères génériques, vous devez désactiver la globalisation avant d'utiliser l' opérateur split + glob dessus et le restaurer pour le$i/$j
et ne pas l' utilisereval
mais l'utiliser"$i"/$j
La réponse @ilkkachu a résolu le principal problème de globbing. Plein crédit à lui.
V1
Cependant, en raison du fait de
exclude
contenir des entrées avec et sans caractère générique (*), et elles peuvent également ne pas exister du tout, une vérification supplémentaire est nécessaire après la globalisation de$i/$j
. Je partage mes conclusions ici.Explication de la sortie
Voici la sortie partielle pour expliquer la situation.
Les éléments ci-dessus sont explicites.
Ce qui précède apparaît car l'entrée d'exclusion (
$j
) n'a pas de caractère générique,$i/$j
devient une concaténation de chaîne simple. Cependant, le fichier / dir n'existe pas.Les éléments ci-dessus apparaissent comme une entrée d'exclusion (
$j
) contient un caractère générique mais n'a aucune correspondance de fichier / répertoire, le globbing de$i/$j
simplement renvoyer la chaîne d'origine.V2
V2 utilise un guillemet simple
eval
etshopt -s nullglob
pour obtenir un résultat net. Aucune vérification finale de fichier / dir ne nécessite.la source
for j in $exclude
, les globes$exclude
pourraient augmenter au moment de cette$exclude
expansion (et invoquereval
cela pose problème). Vous souhaitez que la globalisation soit activée pourfor i in $dir
, etfor l in $k
, mais pas pourfor j in $exclude
. Vous voudriez unset -f
avant ce dernier et unset +f
pour l'autre. Plus généralement, vous voudriez régler votre opérateur split + glob avant de l'utiliser. Dans tous les cas, vous ne voulez pas que split + glob soit utiliséecho $l
,$l
vous devez donc le citer ici.exclude
etdirs
sont en guillemet simple (), so no globbing till
eval`.foo=*
etfoo='*'
c'est pareil. Maisecho $foo
etecho "$foo"
ne le sont pas (dans des coquilles commebash
, il a été corrigé dans des coquilles comme zsh, fish ou rc, voir aussi le lien ci-dessus). Ici, vous ne voulez utiliser cet opérateur, mais à certains endroits que la partie scindée, et dans d'autres seulement la partie globale.Avec
zsh
:${^array}string
est d'étendre au fur et à mesure$array[1]string $array[2]string...
.$=var
consiste à séparer les mots de la variable (quelque chose que les autres shells font par défaut!),$~var
fait de la globalisation sur la variable (quelque chose d'autres shells également par défaut (quand vous ne le souhaitez généralement pas, vous auriez dû citer$f
ci-dessus dans autres coquilles)).(N)
est un qualificatif de glob qui active nullglob pour chacun de ces globs résultant de cette$^array1/$^array2
expansion. Cela fait que les globes s'étendent à rien lorsqu'ils ne correspondent pas. Cela se produit également pour transformer un non-glob~/code/private/foo/Thumbs.db
en un, ce qui signifie que si ce particulier n'existe pas, il n'est pas inclus.la source
exclude
est enfermé affecte la sortie.$^array
doit être fait en deux étapes distinctes pour s'assurer que les éléments vides sont jetés (voir éditer). Cela ressemble un peu à un bugzsh
, je vais soulever le problème sur leur liste de diffusion.