Systeminfo
OS: OS X
bash: GNU bash, versjon 3.2.57 (1) -release (x86_64-apple-darwin16)
Bakgrunn
Jeg vil at tidsmaskinen skal ekskludere et sett med kataloger og filer fra alt mitt git / nodejs-prosjekt. Prosjektkatalogene mine er i ~/code/private/
og ~/code/public/
så jeg prøver å bruk bash looping for å gjøre tmutil
.
Utgave
Kortversjon
Hvis jeg har en kalkulert strengvariabel k
, hvordan får jeg den til å klippe inn eller rett før en 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
I den lange versjonen nedenfor ser du k=$i/$j
. Så jeg kan ikke hardkode strengen i for loop.
Lang versjon
#!/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
Utgang
De er ikke globbed. Ikke hva jeg vil.
~/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
Kommentarer
Svar
Du kan tvinge en ny evalueringsrunde med eval
, men det er faktisk ikke nødvendig. (Og eval
starter har alvorlige problemer i det øyeblikket filnavnene inneholder spesialtegn som $
.) Problemet er ikke med globbing, men med tilde-utvidelsen.
Globbing skjer etter variabel utvidelse, hvis variabelen ikke er sitert, som her (*) :
$ x="/tm*" ; echo $x /tmp
Så, i samme retning, dette ligner på det du gjorde, og fungerer:
$ 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
Men med tilde gjør det ikke t:
$ i="~/public/*"; j="*.launch"; k="$i/$j" $ echo $k ~/public/*/*.launch
Dette er tydelig dokumentert for Bash:
Rekkefølgen av utvidelser er: spenneutvidelse; tildeutvidelse, parameter og variabel utvidelse, …
Tilde-utvidelse skjer før variabel utvidelse, slik at tildes i variabler ikke utvides. Den enkle løsningen er å bruke $HOME
eller hele banen i stedet.
(* å utvide globs fra variabler er vanligvis ikke det du vil ha)
En annen ting:
Når du løper over mønstrene, som her:
exclude="foo *bar" for j in $exclude ; do ...
Vær oppmerksom på at da $exclude
ikke er sitert, er den både delt, og også globbed på dette punktet. Så hvis den nåværende katalogen inneholder noe som samsvarer med mønsteret, utvides det til det:
$ 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
For å omgå dette, bruk en arrayvariabel i stedet for en splittet streng:
$ 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
Som en ekstra bonus kan matriseoppføringer også inneholde mellomrom uten problemer med splitting.
Noe lignende kan gjøres med find -path
, hvis du ikke har noe imot hvilket katalognivå de målrettede filene skal være. F.eks. for å finne en sti som slutter på /e2e/*.js
:
$ dirs="$HOME/public $HOME/private" $ pattern="*/e2e/*.js" $ find $dirs -path "$pattern" /home/foo/public/one/two/three/e2e/asdf.js
Vi må bruke $HOME
i stedet for ~
av samme grunn som før, og $dirs
må ikke siteres på find
kommandolinje slik at den blir delt, men $pattern
bør siteres slik at den ikke ved et uhell utvides av skallet.
(Jeg tror du kan leke med -maxdepth
på GNU og finne for å begrense hvor dypt søket går, hvis du bryr deg, men det er litt av et annet problem.)
Kommentarer
- Er du svaret med
find
? Jeg utforsker faktisk den ruten også, ettersom for-loop blir komplisert. Men jeg har problemer med ‘ -stien ‘. - Takk til deg som din informasjon om tilde ‘ ~ ‘ er mer direkte til hovedspørsmålet. Jeg vil legge ut det endelige manuset og forklaringen i et annet svar. Men full ære for deg: D
- @JohnSiu, ja, bruk av finne var det som først kom opp i tankene dine. Det kan også være brukbart, avhengig av nøyaktig behov. (eller bedre også, for noen bruksområder.)
- @kevinarpe, jeg tror matriser i utgangspunktet er ment for nettopp det, og ja,
"${array[@]}"
(med sitatene! ) er dokumentert (se her og her ) for å utvide til elementene som forskjellige ord uten splitte dem videre. - @sixtyfive, vel,
[abc]
er en standard del av globmønstre , som?
, jeg tror ikke ‘ Jeg tror ikke det ‘ er nødvendig å dekke dem alle her .
Svar
Du kan lagre det som en matrise i stedet for en streng som du kan bruke senere i mange tilfeller og la klatringen skje når du definerer det. I ditt tilfelle, for eksempel:
k=(~/code/public/*/*.launch) for i in "${k[@]}"; do
eller i det senere eksemplet, du «må eval
noen av strengene
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
Kommentarer
- Legg merke til hvordan
$exclude
inneholder jokertegn, du ‘ d trenger å deaktivere globbing før du bruker split + glob operatøren på den og gjenoppretter den for$i/$j
og ikke brukeval
men bruk"$i"/$j
- Både du og ilkkachu gir godt svar. Men svaret hans identifiserte problemet. Så æren til ham.
Svar
@ilkkachu svar løste hovedspørsmålet. Full ære til ham.
V1
På grunn av exclude
som inneholder oppføringer både med og uten jokertegn (*), og de eksisterer kanskje ikke I det hele tatt er det behov for ekstra kontroll etter globbing av $i/$j
. Jeg deler funnene mine her.
#!/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
Forklaringer om utdata
Følgende er den delvise utdata for å forklare situasjonen.
/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! ^^^
Ovenstående er selvforklarende.
/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
Ovenstående vises fordi ekskluderingsoppføringen ($j
) har ikke et jokertegn, $i/$j
blir en ren streng sammenkobling. Filen / dir eksisterer imidlertid ikke.
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.js /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.map
Ovenstående vises som ekskluder oppføring ($j
) inneholder jokertegn, men har ingen samsvar med fil / katalog, globbing av $i/$j
bare returner den opprinnelige strengen.
V2
V2 bruk enkelt sitat, eval
og shopt -s nullglob
for å få et rent resultat. Ingen endelig kontroll av fil / dir krever.
#!/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
Kommentarer
- Et problem er at i
for j in $exclude
, globene i$exclude
kunne utvides på tidspunktet for den$exclude
utvidelsen (og ringereval
på det som ber om problemer). Du ‘ vil at globbing skal være aktivert forfor i in $dir
, ogfor l in $k
, men ikke forfor j in $exclude
. Du ‘ vil ha enset -f
før sistnevnte og enset +f
for den andre. Mer generelt vil du ‘ innstille split + glob-operatøren før du bruker den. I alle fall vil du ikke ‘ ikke vil dele + glob forecho $l
, så$l
skal siteres der. - @St é phaneChazelas refererer du til v1 eller v2? For v2 er både
exclude
ogdirs
i ett sitat (), so no globbing till
eval`. - Globbing foregår ved ikke sitert variabel utvidelse i listesammenhenger , at (å la en variabel ikke siteres) er det vi noen ganger kaller operatøren split + glob . ‘ er ingen globbing i tilordninger til skalarvariabler.
foo=*
ogfoo='*'
er det samme. Menecho $foo
ogecho "$foo"
er ikke (i skjell sombash
, det ‘ er fikset i skjell som zsh, fisk eller rc, se også lenken over). Her gjør du vil bruke den operatøren, men noen steder bare den delte delen, og andre bare glob-delen. - @St é phaneChazelas Takk for info !!! Tok meg en gang, men jeg forstår bekymringen nå. Dette veldig verdifullt !! Takk skal du ha !!!
Svar
Med zsh
:
exclude=" *.launch .classpath .sass-cache Thumbs.db ... " dirs=( ~/code/private/* ~/code/public/* ) for f ($^dirs/${^${=~exclude}}(N)) { echo $f }
${^array}string
er å utvide som $array[1]string $array[2]string...
. $=var
er å utføre orddeling på variabelen (noe andre skjell gjør som standard!), $~var
gjør globbing på variabelen (noe annet skjell også som standard (når du vanligvis ikke vil ha dem til, måtte du sitere $f
ovenfor i andre skjell)).
(N)
er en glob-kvalifisering som slår på nullglob for hver av disse globene som følge av at $^array1/$^array2
utvidelse. Det gjør at globene utvides til ingenting når de ikke samsvarer. Det skjer også for å gjøre en ikke-glob som ~/code/private/foo/Thumbs.db
til en, noe som betyr at hvis det ikke eksisterer, det er ikke inkludert.
Kommentarer
- Dette er veldig hyggelig. Jeg testet og fungerer. Det virker imidlertid som om zsh er mer følsomt for newline når bruker enkelt sitat. Måten
exclude
er vedlagt påvirker utgangen. - @JohnSiu, å ja, du ‘ har rett. Det ser ut til at split + glob og
$^array
må gjøres i to separate trinn for å sikre at de tomme elementene blir kastet (se redigering). Det ser litt ut som en feil izsh
, jeg ‘ Jeg tar opp problemet på adresselisten deres. - Jeg kommer med en v2 for bash, som er renere, men fortsatt ikke så kompakt som zsh-skriptet ditt, lol
Svar
Gammel post men jeg snublet inn her så t trodde dette kan hjelpe andre 🙂
Det er en kinda-glob () -funksjon i bash … den heter compgen -G Den gir en verdi per linje så trenger en readarray for å fungere. p>
Prøv dette:
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
er en beregnet streng, og jeg trenger den sta y på den måten til løkken. Vennligst sjekk den lange versjonen min.