Hogyan lehet a bash glob karakterlánc változót létrehozni?

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

  • Az egyes idézetek megállítják a shell interpolációját a Bash-ban, ezért megpróbálhatja duplán idézni a változót.
  • @ThomasN nem, ez nem működik. 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.
  • @ThomasN Frissítettem a rövid verziót, hogy világosabb legyen.

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álatra eval, 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én eval amely problémát kér). ‘ engedélyezni szeretné, hogy a for i in $dir és a for l in $k esetén a globbing engedélyezve legyen, de a for j in $exclude. ‘ szeretne egy set -f -t az utóbbi elé, a másikra pedig egy set +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 a echo $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 a dirs 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 A foo='*' ugyanaz. De echo $foo és echo "$foo" nem (olyan kagylóban, mint a bash, ‘ 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 egy zsh 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 

Vélemény, hozzászólás?

Az email címet nem tesszük közzé. A kötelező mezőket * karakterrel jelöltük