Systeminfo
Betriebssystem: OS X
Bash: GNU Bash, Version 3.2.57 (1) -release (x86_64-apple-darwin16)
Hintergrund
Ich möchte, dass die Zeitmaschine eine Reihe von Verzeichnissen und Dateien aus allen meinen git / nodejs-Projekten ausschließt. Meine Projektverzeichnisse befinden sich in ~/code/private/
und ~/code/public/
, also versuche ich es Verwenden Sie die Bash-Schleife, um die tmutil
durchzuführen.
Problem
Kurzversion
Wenn ich eine berechnet Zeichenfolgenvariable k
, wie mache ich es global in oder direkt vor einem for- Schleife:
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
In der langen Version unten sehen Sie k=$i/$j
. Daher kann ich die Zeichenfolge nicht fest codieren die for-Schleife.
Lange Version
#!/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
Ausgabe
Sie sind nicht globbed. Nicht das, was ich will.
~/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
Kommentare
- Einfache Anführungszeichen stoppen die Shell-Interpolation in Bash, daher können Sie versuchen, Ihre Variable in doppelte Anführungszeichen zu setzen.
- @ThomasN nein, das funktioniert nicht.
k
ist eine berechnete Zeichenfolge, und ich brauche sie sta y so bis zur Schleife. Bitte überprüfen Sie meine lange Version. - @ThomasN Ich habe die kurze Version aktualisiert, um sie klarer zu machen.
Antwort
Sie können eine weitere Bewertungsrunde mit eval
erzwingen, aber das ist eigentlich nicht erforderlich. (Und eval
startet Sie haben ernsthafte Probleme, sobald Ihre Dateinamen Sonderzeichen wie $
enthalten.) Das Problem liegt nicht beim Globbing, sondern bei der Tilde-Erweiterung.
Globbing tritt auf nach Variablenerweiterung, wenn die Variable nicht in Anführungszeichen gesetzt ist, wie hier (*) :
$ x="/tm*" ; echo $x /tmp
Also, In der gleichen Weise ähnelt dies dem, was Sie getan haben, und funktioniert:
$ mkdir -p ~/public/foo/ ; touch ~/public/foo/x.launch $ i="$HOME/public/*"; j="*.launch"; k="$i/$j" $ echo $k /home/foo/public/foo/x.launch
Aber mit der Tilde funktioniert es nicht „t:
$ i="~/public/*"; j="*.launch"; k="$i/$j" $ echo $k ~/public/*/*.launch
Dies ist klar dokumentiert für Bash:
Die Reihenfolge der Erweiterungen lautet: Klammererweiterung; Tildeerweiterung, Parameter- und Variablenerweiterung, …
Die Tilde-Erweiterung erfolgt vor der Variablenerweiterung, sodass Tildes innerhalb von Variablen nicht erweitert werden. Die einfache Problemumgehung besteht darin, stattdessen $HOME
oder den vollständigen Pfad zu verwenden.
(* Das Erweitern von Globs aus Variablen ist normalerweise nicht das, was Sie möchten)
Eine andere Sache:
Wenn Sie die Muster wie hier durchlaufen:
exclude="foo *bar" for j in $exclude ; do ...
Beachten Sie, dass $exclude
nicht in Anführungszeichen gesetzt ist, an dieser Stelle sowohl geteilt als auch globiert wird. Wenn das aktuelle Verzeichnis also etwas enthält, das dem Muster entspricht, wird es wie folgt erweitert:
$ i="$HOME/public/foo" $ exclude="*.launch" $ touch $i/real.launch $ for j in $exclude ; do # split and glob, no match echo "$i"/$j ; done /home/foo/public/foo/real.launch $ touch ./hello.launch $ for j in $exclude ; do # split and glob, matches in current dir! echo "$i"/$j ; done /home/foo/public/foo/hello.launch # not the expected result
Um dies zu umgehen, verwenden Sie eine Array-Variable anstelle einer geteilten Zeichenfolge:
$ exclude=("*.launch") $ exclude+=("something else") $ for j in "${exclude[@]}" ; do echo "$i"/$j ; done /home/foo/public/foo/real.launch /home/foo/public/foo/something else
Als zusätzlichen Bonus können Array-Einträge auch Leerzeichen ohne Probleme beim Teilen enthalten.
Ähnliches könnte getan werden find -path
, wenn Sie nicht wissen, auf welcher Verzeichnisebene die Zieldateien sein sollen. Suchen Sie beispielsweise nach einem Pfad, der auf /e2e/*.js
endet:
$ dirs="$HOME/public $HOME/private" $ pattern="*/e2e/*.js" $ find $dirs -path "$pattern" /home/foo/public/one/two/three/e2e/asdf.js
Wir müssen $HOME
anstelle von ~
aus demselben Grund wie zuvor, und $dirs
muss auf dem find
Befehlszeile, damit es geteilt wird, aber $pattern
sollte in Anführungszeichen gesetzt werden, damit es nicht versehentlich von der Shell erweitert wird.
(Ich denke, Sie könnten mit -maxdepth
auf GNU find spielen, um zu begrenzen, wie tief die Suche geht, wenn Sie sich interessieren, aber das ist ein etwas anderes Problem.)
Kommentare
- Sind Sie die einzige Antwort mit
find
? Ich erkunde diese Route auch, da die for-Schleife immer komplizierter wird. Ich habe jedoch Probleme mit dem ‚ -Pfad ‚. - Gutschrift als Ihre Information über Tilde ‚ ~ ‚ bezieht sich direkter auf das Hauptproblem. Ich werde das endgültige Skript und die Erklärung in einer anderen Antwort veröffentlichen. Aber volle Anerkennung für Sie: D
- @JohnSiu, ja, die Verwendung von find war das, woran Sie zuerst gedacht haben. Je nach Bedarf kann es auch verwendet werden. (oder besser auch für einige Zwecke.)
- @kevinarpe, ich denke, Arrays sind im Grunde genommen genau dafür gedacht, und ja,
"${array[@]}"
(mit den Anführungszeichen! ) wird dokumentiert (siehe hier und hier ), um die Elemente als eindeutige Wörter ohne zu erweitern sie weiter aufteilen. - @sixtyfive, na ja,
[abc]
ist ein Standardteil von Glob-Mustern wie?
, ich ‚ denke nicht, dass es ‚ notwendig ist, alle hier abzudecken .
Antwort
Sie können es als Array anstelle einer Zeichenfolge speichern, die später in vielen Fällen verwendet werden soll Lassen Sie das Globbing geschehen, wenn Sie es definieren. In Ihrem Fall zum Beispiel:
k=(~/code/public/*/*.launch) for i in "${k[@]}"; do
oder im späteren Beispiel Sie „Ich muss eval
einige der Zeichenfolgen
dirs=(~/code/private/* ~/code/public/*) for i in "${dirs[@]}"; do for j in $exclude; do eval "for k in $i/$j; do tmutil addexclusion \"\$k\"; done" done done
Kommentare
- Beachten Sie, wie
$exclude
Platzhalter enthält, Sie ‚ d muss das Globbing deaktivieren, bevor der Operator split + glob verwendet wird, und es für die Verwendung$i/$j
und not wiederherstelleneval
aber verwenden Sie"$i"/$j
- Sowohl Sie als auch ilkkachu geben eine gute Antwort. Seine Antwort identifizierte jedoch das Problem an ihn.
Antwort
@ilkkachu Antwort löste das Hauptproblem der Globbing. Volle Anerkennung für ihn.
V1
Aufgrund von exclude
, die Einträge mit und ohne Platzhalter (*) enthalten, sind sie möglicherweise auch nicht vorhanden Insgesamt ist nach dem Globbing von $i/$j
eine zusätzliche Überprüfung erforderlich. Ich teile meine Ergebnisse hier.
#!/bin/bash exclude=" *.launch .DS_Store .classpath .sass-cache .settings 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=" $HOME/code/private/* $HOME/code/public/* " # loop $dirs for i in $dirs; do for j in $exclude ; do for k in $i/$j; do echo -e "$k" if [ -f $k ] || [ -d $k ] ; then # Only execute command if dir/file exist echo -e "\t^^^ Above file/dir exist! ^^^" fi done done done
Erklärung der Ausgabe
Es folgt die Teilausgabe zur Erläuterung der Situation.
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/a.launch ^^^ Above file/dir exist! ^^^ /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/b.launch ^^^ Above file/dir exist! ^^^ /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.DS_Store ^^^ Above file/dir exist! ^^^
Die obigen Angaben sind selbsterklärend.
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.classpath /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.sass-cache /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/.settings /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/Thumbs.db /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/bower_components /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/build /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/connect.lock /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/coverage /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/dist
Die obigen Angaben werden angezeigt, weil der Ausschlusseintrag ($j
) hat keinen Platzhalter, $i/$j
wird zu einer einfachen Zeichenfolgenverkettung. Die Datei / das Verzeichnis ist jedoch nicht vorhanden.
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.js /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.map
Die obigen Angaben werden als Ausschlusseintrag ($j
) angezeigt Platzhalter, aber keine Datei- / Verzeichnisübereinstimmung. Beim Globbing von $i/$j
wird nur die ursprüngliche Zeichenfolge zurückgegeben.
V2
V2 Verwenden Sie ein einfaches Anführungszeichen. eval
und shopt -s nullglob
, um ein sauberes Ergebnis zu erhalten. Es ist keine endgültige Überprüfung der Datei / des Verzeichnisses erforderlich.
#!/bin/bash exclude=" *.launch .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=" $HOME/code/private/* $HOME/code/public/* " for i in $dirs; do for j in $exclude ; do shopt -s nullglob eval "k=$i/$j" for l in $k; do echo $l done shopt -u nullglob done done
Kommentare
- Ein Problem ist das in
for j in $exclude
, die Globs in$exclude
könnten zum Zeitpunkt dieser$exclude
-Erweiterung (und Aufruf) erweitert werdeneval
fragt nach Problemen). Sie ‚ möchten, dass das Globbing fürfor i in $dir
undfor l in $k
aktiviert wird, jedoch nicht fürfor j in $exclude
. Sie ‚ möchten eineset -f
vor der letzteren und eineset +f
für die andere. Im Allgemeinen möchten Sie ‚ Ihren split + glob-Operator optimieren, bevor Sie ihn verwenden. In jedem Fall möchten Sie ‚ nicht split + glob fürecho $l
, also$l
sollte dort zitiert werden. - @St é phaneChazelas beziehen Sie sich auf v1 oder v2? Für v2 stehen sowohl
exclude
als auchdirs
in einfachen Anführungszeichen (), so no globbing till
eval`. - Globbing findet bei nicht zitierter Variable Expansion in Listenkontexten statt. Das ist (was eine Variable nicht zitiert), was wir tun Rufen Sie manchmal den Operator split + glob auf. ‚ gibt es kein Globbing bei Zuweisungen zu skalaren Variablen.
foo=*
undfoo='*'
ist dasselbe.echo $foo
undecho "$foo"
sind dies jedoch nicht (in Shells wiebash
, es ‚ wurde in Muscheln wie zsh, fish oder rc behoben, siehe auch den obigen Link). Hier tun Sie möchte diesen Operator verwenden, aber an einigen Stellen nur den geteilten Teil und an anderen nur den Glob-Teil. - @St é phaneChazelas Vielen Dank für Die Info !!! Hat mich irgendwann gekostet, aber ich verstehe die Besorgnis jetzt. Das ist sehr wertvoll !! Danke !!!
Antwort
Mit zsh
:
exclude=" *.launch .classpath .sass-cache Thumbs.db ... " dirs=( ~/code/private/* ~/code/public/* ) for f ($^dirs/${^${=~exclude}}(N)) { echo $f }
${^array}string
soll als $array[1]string $array[2]string...
. $=var
führt eine Wortaufteilung für die Variable durch (etwas, das andere Shells standardmäßig tun!). $~var
führt ein Globbing für die Variable durch (etwas anderes) Shells auch standardmäßig (wenn Sie dies im Allgemeinen nicht möchten, müssten Sie in anderen Shells $f
oben zitieren)).
(N)
ist ein Glob-Qualifizierer, der nullglob für jeden dieser Globs aktiviert, die sich aus diesem $^array1/$^array2
Erweiterung. Das führt dazu, dass sich die Globs zu nichts ausdehnen, wenn sie nicht übereinstimmen. Das macht auch einen Nicht-Glob wie ~/code/private/foo/Thumbs.db
zu einem, was bedeutet, dass wenn dieser bestimmte nicht existiert, es ist nicht enthalten.
Kommentare
- Das ist wirklich schön. Ich habe getestet und funktioniert. Es scheint jedoch, dass zsh empfindlicher auf Zeilenumbrüche reagiert, wenn Die Art und Weise, wie
exclude
eingeschlossen ist, wirkt sich auf die Ausgabe aus. - @JohnSiu, oh ja, Sie ‚ div Es scheint, dass split + glob und
$^array
in zwei separaten Schritten ausgeführt werden müssen, um sicherzustellen, dass die leeren Elemente verworfen werden (siehe Bearbeiten). Das sieht ein bisschen wie a aus Fehler inzsh
, ich ‚ werde das Problem auf ihrer Mailingliste ansprechen. - Ich habe eine v2 für bash, das sauberer ist, aber immer noch nicht so kompakt wie Ihr zsh-Skript, lol
Antwort
Alt post aber ich bin hier so t gestolpert dachte, dies könnte anderen helfen 🙂
Es gibt eine kinda-glob () -Funktion in bash … sie heißt compgen -G. Sie gibt einen Wert pro Zeile aus und benötigt daher ein Readarray, um zu funktionieren.
Versuchen Sie Folgendes:
i="~/code/public/*" j="*.launch" k=$i/$j # $k="~/code/public/*/*.launch" readarray -t files < <(compgen -G "$k") # $k is globbed here for i in "${files[@]}" do echo "Found -$i-" done