Rendszerinformáció
OS: OS X
bash: GNU bash, 3.2.57 verzió (1) -release (x86_64-apple-darwin16)
Háttér
Azt akarom, hogy az időgép kizárjon egy könyvtárat és fájlt az összes git / nodejs projektemből. A projektkönyvtárak ~/code/private/
és ~/code/public/
könyvtárban vannak, így megpróbálom a bash looping használatával hajtsa végre a tmutil
fájlt.
Probléma
Rövid verzió
Ha van
kiszámított karakterlánc változó k
, hogyan tehetem azt globálisá vagy jobbra egy for- loop:
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
Az alábbi hosszú változatban látni fogja a k=$i/$j
elemet. Tehát nem tudom kódolni a karakterláncot a for ciklus.
Hosszú verzió
#!/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
Output
Nem kerülnek ki. Nem az, amit akarok.
~/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
Megjegyzések
Válasz
Újabb értékelési kört kényszeríthet a eval
gombbal, de ez valójában nem szükséges. (És eval
elindul komoly problémái vannak abban a pillanatban, amikor a fájlnevek olyan speciális karaktereket tartalmaznak, mint a $
.) A probléma nem a globbolással, hanem a tilde kibővítésével van.
A globbing történik változó kibővítése után, ha a változó nem szerepel, mint itt (*) :
$ x="/tm*" ; echo $x /tmp
Tehát, ugyanebben az értelemben hasonlít arra, amit tettél, és működik:
$ 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
De a tildével nem “t”:
$ i="~/public/*"; j="*.launch"; k="$i/$j" $ echo $k ~/public/*/*.launch
Ez egyértelműen dokumentált a Bash számára:
A kiterjesztések sorrendje: zárójel-kiterjesztés; tilde-kiterjesztés, paraméter- és változó-kiterjesztés, …
A Tilde kiterjesztése a változó kiterjesztése előtt történik, így a változókon belüli Tilde nem bővül. Az egyszerű megoldás a $HOME
vagy a teljes elérési út használata.
(* a globusok változóból történő kibővítése általában nem az, amit akar)
Másik dolog:
Amikor átmásolod a mintákat, mint itt:
exclude="foo *bar" for j in $exclude ; do ...
vegye figyelembe, hogy mivel az $exclude
nincs megadva, ezért mindkettő kettéhasad, és ezen a ponton is felcsendül. Tehát ha az aktuális könyvtár tartalmaz valamit, ami illeszkedik a mintához, akkor ez kibővült:
$ 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
Ennek kiküszöböléséhez használjon egy tömb változót szétválasztott karakterlánc helyett:
$ 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
További bónuszként a tömbbejegyzések tartalmazhatnak szóközt is, felosztás nélkül.
Valami hasonlót meg lehetne csinálni find -path
, ha nem bánja, hogy a megcélzott fájloknak milyen könyvtárszintűeknek kell lenniük. Pl. a /e2e/*.js
végződésű utak megtalálásához:
$ dirs="$HOME/public $HOME/private" $ pattern="*/e2e/*.js" $ find $dirs -path "$pattern" /home/foo/public/one/two/three/e2e/asdf.js
A $HOME
a ~
helyett ugyanazon okból, mint korábban, és a $dirs
elemet fel kell tüntetni a find
parancssor, így fel lesz osztva, de a $pattern
-t idézni kell, így véletlenül nem bővül ki a héj által.
(Azt hiszem, játszhatna a -maxdepth
vel a GNU keresőben, hogy korlátozza a keresés mélységét, ha érdekel, de ez “kicsit más kérdés.”
Megjegyzések
- Ön az egyetlen válasz a következővel:
find
? Valójában ezt az útvonalat is felfedezem, mivel a for-loop egyre bonyolultabb. De nehézségeim vannak a ‘ -path ‘ problémával. - Köszönjük, hogy a tilde adatairól ‘ ~ ‘ közvetlenebb a fő kérdésre. A végleges forgatókönyvet és magyarázatot egy másik válaszban teszem közzé. De teljes elismerés: D
- @JohnSiu, igen, a találat használata volt az, ami először eszembe jutott. Lehet, hogy a pontos igénytől függően is használható. (vagy jobb is, néhány felhasználásra.)
- @kevinarpe, azt gondolom, hogy a tömbök alapvetően csak ezt jelentik, és igen,
"${array[@]}"
(az idézőjelekkel! ) dokumentálva van (lásd itt és itt ), hogy az elemekre külön szavakként bővülhessen tovább hasítva őket. - @sixtyfive, nos, a
[abc]
a globális minták szokásos része, például?
, nem hiszem, hogy ‘ nem gondolom, hogy ‘ el kell mennem mindet ide .
Válasz
Tömbökként mentheti el karakterlánc helyett, hogy később később sok esetben használhassa, és hagyja, hogy a globbing történjen, amikor meghatározza. Az Ön esetében például:
k=(~/code/public/*/*.launch) for i in "${k[@]}"; do
vagy a későbbi példában “eval
néhány karakterláncot kell
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
megjegyzések
- Vegye figyelembe, hogy a
$exclude
hogyan tartalmaz helyettesítő karaktereket, te ‘ d-nek le kell tiltania a hamisítást, mielőtt a rajta lévő split + glob operátort használni szeretné, és vissza kell állítania a$i/$j
és nem használatraeval
, de használja a"$i"/$j
- Ön és ilkkachu egyaránt jó választ ad. Válasza azonban azonosította a problémát. Tehát hitel neki.
Válasz
A @ilkkachu válasz megoldotta a fő hibát. Teljes elismerés neki.
V1
Azonban a exclude
miatt helyettesítő karakterrel és helyettesítővel (*) rendelkező bejegyzéseket is tartalmaz, és előfordulhat, hogy nem is léteznek Összességében további ellenőrzésre van szükség az $i/$j
beugrása után. Itt osztom meg megállapításaimat.
#!/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
Kimenet magyarázata
Az alábbiakban bemutatjuk a helyzet magyarázatának részkimenetét.
/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! ^^^
A fentiek önmagukban értendők.
/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
A fentiek azért jelennek meg, mert a kizáró bejegyzés ($j
) nincs helyettesítő karakter, $i/$j
sima karakterlánc összefűzéssé válik. A fájl / dir azonban nem létezik.
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.js /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.map
A fentiek kizáró bejegyzésként jelennek meg ($j
) tartalmazzák helyettesítő karakter, de nincs fájl / könyvtár egyezés, az $i/$j
globbingja csak visszaadja az eredeti karakterláncot.
V2
V2 egyetlen idézetet használ, eval
és shopt -s nullglob
a tiszta eredmény elérése érdekében. Nincs szükség fájl / dir utolsó ellenőrzésre.
#!/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
Megjegyzések
- Az egyik probléma az, hogy a
for j in $exclude
, a$exclude
ben található globusok kibővülhetnek a$exclude
bővítés (és hívás) idejéneval
amely problémát kér). ‘ engedélyezni szeretné, hogy afor i in $dir
és afor l in $k
esetén a globbing engedélyezve legyen, de afor j in $exclude
. ‘ szeretne egyset -f
-t az utóbbi elé, a másikra pedig egyset +f
-t. Általánosabban véve, ‘ hangolni szeretné a split + glob operátort, mielőtt használná. Mindenesetre nem ‘ nem akar split + globot használni aecho $l
számára, tehát$l
ott kell idézni. - @St é phaneChazelas v1-re vagy v2-re hivatkozik? A v2 esetében mind a
exclude
, mind adirs
egyetlen idézetben szerepel (), so no globbing till
eval`. - A globbing a jegyzék nélküli kontextusban a nem jegyzett változó kibővítése után következik be , hogy (ha egy változó nem szerepel) néha felhívja a split + glob operátort. ‘ nincs skálázó változóhoz rendelt hozzárendelés.
foo=*
és Afoo='*'
ugyanaz. Deecho $foo
ésecho "$foo"
nem (olyan kagylóban, mint abash
, ‘ olyan kagylókban rögzítették, mint a zsh, a fish vagy az rc, lásd a fenti linket is.) Itt csinál azt az operátort akarja használni, de néhol csak a felosztott részt, másutt csak a globális részt. - @St é phaneChazelas Köszönjük az információ !!! Vitt valamikor, de most már értem az aggodalmat. Ez nagyon értékes !! Köszönöm !!! l>
Válasz
A következővel: zsh
:
exclude=" *.launch .classpath .sass-cache Thumbs.db ... " dirs=( ~/code/private/* ~/code/public/* ) for f ($^dirs/${^${=~exclude}}(N)) { echo $f }
${^array}string
bővíteni kell, mint $array[1]string $array[2]string...
. A $=var
a szó felosztásának végrehajtása a változón (ezt alapértelmezésben más héjak is megteszik!), a $~var
a változót lobogtatja (valami más a héjak alapértelmezés szerint is (amikor általában nem akarja őket, akkor a fenti $f
t kellett idéznie más héjakban)).
(N)
egy globális minősítő, amely a $^array1/$^array2
nullglob t az egyes globusoknál
bővítés. Ez arra készteti a földgömböket, hogy a semmibe táguljanak, ha nem egyeznek. Ez egy olyan nem-gömböt is átalakít, mint a~/code/private/foo/Thumbs.db
, ami azt jelenti, hogy ha az adott nem létezik, nincs benne.
Megjegyzések
- Ez nagyon szép. Teszteltem és működik. Úgy tűnik azonban, hogy a zsh érzékenyebb az új vonalakra, amikor egyetlen idézet használata. A
exclude
bezárása befolyásolja a kimenetet. - @JohnSiu, ja, igen, te ‘ igaza van. Úgy tűnik, hogy a split + glob-ot és a
$^array
-et két külön lépésben kell elvégezni annak biztosítása érdekében, hogy az üres elemeket eldobják (lásd a szerkesztést). Ez kicsit hasonlít egyzsh
hiba, ‘ felvetem a problémát a levelezőlistájukon. - előállok egy v2-vel A bash esetében, amely tisztább, de még mindig nem olyan kompakt, mint a zsh szkripted, lol
Válasz
Régi poszt, de itt botlottam be, így t gondoltam, ez másoknak is segíthet 🙂
A bash-ban van egy kinda-glob () függvény … compgen -G-nek hívják. Soronként ad ki egy értéket, így a működéshez újra kell írni.
Próbálja ki:
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
k
egy számított karakterlánc, és nekem szükségem van rá y így a hurokig. Kérjük, ellenőrizze a hosszú verziómat.