Kuinka tehdä bash globista merkkijonomuuttuja?

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

  • Yksittäiset lainausmerkit pysäyttävät kuoren interpoloinnin Bashissa, joten voit yrittää kaksinkertaistaa lainausmerkin muuttujaasi.
  • @ThomasN ei, se ei toimi. k on laskettu merkkijono, ja tarvitsen sitä sta y näin silmukkaan asti. Tarkista pitkä versioni.
  • @ThomasN Päivitin lyhyen version selkeämmäksi.

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 aikaan eval, joka pyytää ongelmia). ’ Haluat, että hölynpöly on käytössä for i in $dir ja for l in $k, mutta et for j in $exclude. ’ haluat set -f ennen jälkimmäistä ja set +f toiselle. Yleisemmin ’ haluat virittää split + glob -operaattorisi ennen sen käyttöä. Joka tapauksessa et halua ’ halua split + globia echo $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. Mutta echo $foo ja echo "$foo" eivät ole (kuorissa kuten bash, 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 vika zsh, 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 

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *