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
$exclude
sisä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 $dir
jafor l in $k
, mutta etfor j in $exclude
. ’ haluatset -f
ennen jälkimmäistä jaset +f
toiselle. Yleisemmin ’ haluat virittää split + glob -operaattorisi ennen sen käyttöä. Joka tapauksessa et halua ’ halua split + globiaecho $l
, joten$l
tulisi lainata siellä. - @St é phaneChazelas viittaatko v1: een tai v2: een? V2: lle sekä
exclude
ettädirs
ovat yhdessä lainauksessa (), so no globbing till
eval`. - 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 $foo
jaecho "$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
exclude
on suljettu, vaikuttaa tulokseen. - @JohnSiu, kyllä, sinä ’ re right. Vaikuttaa siltä, että split + glob ja
$^array
on 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
k
on laskettu merkkijono, ja tarvitsen sitä sta y näin silmukkaan asti. Tarkista pitkä versioni.