Järjestelmätiedot
Käyttöjärjestelmä: OS X
bash: GNU bash, versio 3.2.57 (1) -release (x86_64-apple-darwin16)
Tausta
Haluan, että aikakone sulkee joukon hakemistoja ja tiedostoja kaikista git / nodejs -projekteistani. Projektihakemistoni ovat ~/code/private/ ja ~/code/public/ , joten yritän käytä bash-silmukointia tmutil.
Ongelma
Lyhyt versio
Jos minulla on laskettu merkkijonomuuttuja k, miten teen sen globaaliksi tai oikealle ennen silmukka:
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
Alla olevassa pitkässä versiossa näet k=$i/$j. Joten en voi koodata merkkijonoa sisään for for -silmukka.
Pitkä versio
#!/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
Niitä ei huijata. Ei mitä haluan.
~/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
Kommentit
Vastaa
Voit pakottaa toisen arviointikierroksen eval -toiminnolla, mutta sitä ei todellakaan tarvita. (Ja eval alkaa sinulla on vakavia ongelmia sillä hetkellä, kun tiedostonimissäsi on erikoismerkkejä, kuten $.) Ongelma ei ole huojumisessa, vaan tilde-laajennuksessa.
Huutelua tapahtuu muuttujan laajennuksen jälkeen, jos muuttujaa ei ole mainittu, kuten tässä (*) :
$ x="/tm*" ; echo $x /tmp
Joten, samalla tavoin tämä on samanlainen kuin teit ja toimii:
$ 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
Mutta tilden kanssa se ei t:
$ i="~/public/*"; j="*.launch"; k="$i/$j" $ echo $k ~/public/*/*.launch
Tämä on selvästi dokumentoitu Bashille:
Laajennusten järjestys on: aaltosuuntaus, tilde-laajennus, parametrien ja muuttujien laajennus, …
Tilde-laajennus tapahtuu ennen muuttujan laajentamista, joten muuttujien sisällä olevia tildejä ei laajenneta. Helppo kiertotapa on käyttää sen sijaan $HOME tai koko polkua.
(* maapallojen laajentaminen muuttujista ei yleensä ole mitä haluat)
Toinen asia:
Kun siirryt mallien yli, kuten tässä:
exclude="foo *bar" for j in $exclude ; do ...
Huomaa, että koska $exclude ei ole lainausmerkkejä, se sekä jakautuu että höyrystyy tässä vaiheessa. Joten jos nykyinen hakemisto sisältää jotain, joka vastaa mallia, se laajeni siihen:
$ 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
Voit kiertää tämän käyttämällä -muuttujaa jaetun merkkijonon sijaan:
$ 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
Lisäbonuksena taulukon merkinnät voivat sisältää myös välilyönnin ilman jakautumisongelmia.
Jotain vastaavaa voitaisiin tehdä find -path, jos et välitä, minkä hakemistotason kohdennettujen tiedostojen tulisi olla. Esimerkiksi löytää kaikki polut, jotka päättyvät /e2e/*.js:
$ dirs="$HOME/public $HOME/private" $ pattern="*/e2e/*.js" $ find $dirs -path "$pattern" /home/foo/public/one/two/three/e2e/asdf.js
Meidän on käytettävä $HOME ~ -kohdan sijaan samasta syystä kuin aiemmin, ja $dirs -kohdassa on oltava noteeraamaton find komentorivi, jotta se jakautuu, mutta $pattern tulisi lainata, jotta komentotulkki ei vahingossa laajenna sitä.
(Luulen, että voisit pelata -maxdepth -palvelun kanssa GNU-haulla, jotta voit rajoittaa haun syvyyttä, jos välität, mutta se on hieman eri asia.)
Kommentit
- Oletko ainoa vastaus käyttäjälle
find? Tutkin itse asiassa myös tätä reittiä, koska for-loop on monimutkainen. Mutta minulla on vaikeuksia ’ -path ’ kanssa. - Kiitos sinulle tietona tildestä ’ ~ ’ on suorempi pääkysymykseen. Lähetän lopullisen käsikirjoituksen ja selityksen toiseen vastaukseen. Mutta täysi kunnianosoitus sinulle: D
- @JohnSiu, joo, etsiminen tuli ensin mieleen. Se voi olla myös käyttökelpoinen, tarkasta tarpeesta riippuen. (tai parempi myös joihinkin käyttötarkoituksiin.)
- @kevinarpe, mielestäni taulukot on tarkoitettu pohjimmiltaan juuri tähän, ja kyllä,
"${array[@]}"(lainausmerkit! ) on dokumentoitu (katso täällä ja täällä ), jotta se voidaan laajentaa elementteihin erillisinä sanoina ilman jakamalla ne edelleen. - @sixtyfive, no,
[abc]on vakio osa maapallokuvioita , kuten?, en ’ usko, että ’ on välttämätöntä mennä kattamaan ne kaikki täällä .
vastaus
Voit tallentaa sen matriisina merkkijonon sijaan käytettäväksi myöhemmin monissa tapauksissa ja anna huojunnan tapahtua, kun määrität sen. Sinun tapauksessasi esimerkiksi:
k=(~/code/public/*/*.launch) for i in "${k[@]}"; do
tai myöhemmässä esimerkissä ”Tarvitsen eval joitain merkkijonoja
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
kommentit
- Huomaa, kuinka
$excludesisältää jokerimerkkejä, sinä ’ d: n on estettävä huijaaminen, ennen kuin siinä käytetään split + glob -operaattoria, ja palautettava se$i/$j– ja ei -käyttöön.eval, mutta käytä"$i"/$j - Sekä sinä että ilkkachu annat hyvän vastauksen. Hänen vastauksestaan kuitenkin tunnistettiin asia. Joten luottoa hänelle.
Vastaus
@ilkkachu vastaus ratkaisi päähämmennyskysymyksen. Täysi kiitos hänelle.
V1
Koska exclude sisältää merkintöjä sekä jokerimerkkien kanssa että ilman niitä, eivätkä ne välttämättä ole olemassa Kaiken kaikkiaan lisätarkistuksia tarvitaan $i/$j: n huijaamisen jälkeen. Jaan havainnot täällä.
#!/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
Tuloksen selitys
Seuraava on osittainen tulos tilanteen selittämiseksi.
/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! ^^^
Yllä olevat ovat itsestään selviä.
/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
Yllä olevat näkyvät, koska poissulkeva merkintä ($j) ei ole jokerimerkkiä, $i/$j tulee tavallinen merkkijono ketjutus. Tiedostoa / dir ei kuitenkaan ole olemassa.
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.js /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.map
Yllä olevat näkyvät poissulkevana merkintänä ($j) sisältävät jokerimerkki, mutta sillä ei ole tiedostoa / hakemistoa, $i/$j -kuvan palautus palauttaa vain alkuperäisen merkkijonon.
V2
V2 käyttää yhtä lainausta, eval ja shopt -s nullglob saadaksesi puhtaan tuloksen. Tiedostoa / suoraa lopputarkistusta ei vaadita.
#!/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
Kommentit
- Yksi ongelma on
for j in $exclude,$exclude-kansiossa olevat maapallot voisivat laajentua kyseisen$exclude-laajennuksen aikaaneval, joka pyytää ongelmia). ’ Haluat, että hölynpöly on käytössäfor i in $dirjafor l in $k, mutta etfor j in $exclude. ’ haluatset -fennen jälkimmäistä jaset +ftoiselle. Yleisemmin ’ haluat virittää split + glob -operaattorisi ennen sen käyttöä. Joka tapauksessa et halua ’ halua split + globiaecho $l, joten$ltulisi lainata siellä. - @St é phaneChazelas viittaatko v1: een tai v2: een? V2: lle sekä
excludeettädirsovat yhdessä lainauksessa (), so no globbing tilleval`. - Höyrystyminen tapahtuu noteeraamattomien muuttujien laajennusten yhteydessä luettelokonteksteissa , että (muuttujan jättäminen tarjoamatta) on se mitä me joskus soita split + glob -operaattorille. ’ ei skandaarimuuttujille osoitetuissa määrityksissä.
foo='*'on sama. Muttaecho $foojaecho "$foo"eivät ole (kuorissa kutenbash, se ’ on kiinnitetty kuoriin kuten zsh, kala tai rc, katso myös yllä oleva linkki). Täällä teet haluavat käyttää kyseistä operaattoria, mutta joissain paikoissa vain jaettu osa ja toisissa vain globaali osa. - @St é phaneChazelas Kiitos info !!! Otti minut joskus, mutta ymmärrän huolen nyt. Tämä on erittäin arvokasta! Kiitos !!!
Vastaa
Kanssa zsh:
exclude=" *.launch .classpath .sass-cache Thumbs.db ... " dirs=( ~/code/private/* ~/code/public/* ) for f ($^dirs/${^${=~exclude}}(N)) { echo $f }
${^array}string on laajeneva nimellä $array[1]string $array[2]string.... $=var on suorittaa sanojen jakaminen muuttujalle (jotain muuta kuoret tekevät oletusarvoisesti!), $~var huijaa muuttujaa (jotain muuta kuoret myös oletusarvoisesti (kun et yleensä halua niitä, sinun on pitänyt lainata yllä $f muissa kuoreissa).
(N) on globaalitunniste, joka ottaa käyttöön nullglob jokaiselle kyseiselle globaalille, joka johtuu siitä, että $^array1/$^array2 laajennus. Tämä saa maapallot laajenemaan mihinkään, kun ne eivät täsmää. Se myös sattuu tekemään muun kuin maapallon ~/code/private/foo/Thumbs.db yhdeksi, mikä tarkoittaa, että jos kyseistä ei ole olemassa, sitä ei sisälly.
Kommentit
- Tämä on todella mukavaa. Testasin ja toimin. Vaikuttaa kuitenkin siltä, että zsh on herkempi uudelle riville, kun käyttämällä yhtä lainausta. Tapa
excludeon suljettu, vaikuttaa tulokseen. - @JohnSiu, kyllä, sinä ’ re right. Vaikuttaa siltä, että split + glob ja
$^arrayon tehtävä kahdessa erillisessä vaiheessa varmistaaksesi, että tyhjät elementit hylätään (katso muokkaus). Se näyttää vähän kuin vikazsh, minä ’ tuon ongelman heidän postituslistalleen. - keksin v2: n bashille, joka on puhtaampi, mutta ei silti yhtä kompakti kuin zsh-komentosarjasi, lol
Vastaa
Vanha mutta törmäsin tänne niin t luulen, että tämä voi auttaa muita 🙂
Bashissa on kinda-glob () -funktio … sitä kutsutaan compgen -G Se antaa arvon riviä kohti, joten se tarvitsee uudelleentarkastelun toimiakseen.
Kokeile tätä:
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
kon laskettu merkkijono, ja tarvitsen sitä sta y näin silmukkaan asti. Tarkista pitkä versioni.