Systeminfo
OS: OS X
bash: GNU bash, version 3.2.57 (1) -release (x86_64-apple-darwin16)
Baggrund
Jeg ønsker, at tidsmaskinen skal udelukke et sæt mapper og filer fra alt mit git / nodejs-projekt. Mine projektmapper findes i ~/code/private/
og ~/code/public/
så jeg prøver at brug bash looping til at udføre tmutil
.
Udgave
Kort version
Hvis jeg har en beregnet strengvariabel k
, hvordan får jeg det til at kugle ind eller lige 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 version nedenfor vil du se k=$i/$j
. Så jeg kan ikke kode strengen i for-sløjfen.
Lang version
#!/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
De er ikke globbed. Ikke hvad 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ødvendigt. (Og eval
starter har alvorlige problemer i det øjeblik, dine filnavne indeholder specialtegn som $
.) Problemet er ikke med globbing, men med tilde-udvidelsen.
Globbing sker efter variabeludvidelse, hvis variablen ikke er citeret, som her (*) :
$ x="/tm*" ; echo $x /tmp
Så, på samme måde svarer det til 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 betyder det ikke:
$ i="~/public/*"; j="*.launch"; k="$i/$j" $ echo $k ~/public/*/*.launch
Dette er tydeligt dokumenteret til Bash:
Rækkefølgen af udvidelser er: afstivningsudvidelse; tildeudvidelse, parameter og variabel udvidelse, …
Tilde-udvidelse sker før variabel udvidelse, så tildes inden i variabler ikke udvides. Den nemme løsning er at bruge $HOME
eller den fulde sti i stedet.
(* at udvide globs fra variabler er normalt ikke det, du vil have)
En anden ting:
Når du løber over mønstrene, som her:
exclude="foo *bar" for j in $exclude ; do ...
bemærk, at da $exclude
ikke citeres, er den både delt og også globbed på dette tidspunkt. Så hvis den aktuelle mappe indeholder noget, der matcher mønsteret, udvides 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 at omgå dette skal du bruge 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 array-poster også indeholde mellemrum uden problemer med opdeling.
Noget lignende kan gøres med find -path
, hvis du ikke har noget imod hvilket katalogniveau de målrettede filer skal være. F.eks. at finde en sti, der slutter med /e2e/*.js
:
$ dirs="$HOME/public $HOME/private" $ pattern="*/e2e/*.js" $ find $dirs -path "$pattern" /home/foo/public/one/two/three/e2e/asdf.js
Vi skal bruge $HOME
i stedet for ~
af samme grund som før, og $dirs
skal citeres på find
kommandolinje, så den bliver delt, men $pattern
bør citeres, så den ikke ved et uheld udvides af skallen.
(Jeg tror, du kunne lege med -maxdepth
på GNU finde ud af at begrænse, hvor dybt søgningen går, hvis du er ligeglad, men det er lidt af et andet problem.)
Kommentarer
- Er du det eneste svar med
find
? Jeg udforsker faktisk den rute også, da for-loop bliver kompliceret. Men jeg har problemer med ‘ -stien ‘. - Kredit til dig som din info om tilde ‘ ~ ‘ er mere direkte til hovedproblemet. Jeg vil sende det endelige script og forklaringen i et andet svar. Men fuld kredit til dig: D
- @JohnSiu, ja, ved hjælp af find var det, der først kom op i tankerne. Det kan også være anvendeligt afhængigt af det nøjagtige behov. (eller bedre for nogle anvendelser.)
- @kevinarpe, jeg tror, at arrays grundlæggende er beregnet til netop det, og ja,
"${array[@]}"
(med citaterne! ) er dokumenteret (se her og her ) for at udvide til elementerne som forskellige ord uden opdele dem yderligere. - @sixtyfive, ja,
[abc]
er en standard del af glob mønstre , som?
, jeg tror ikke ‘ Jeg tror ikke det ‘ er nødvendigt at dække dem alle her .
Svar
Du kan gemme det som en matrix i stedet for en streng til at bruge senere i mange tilfælde og lad kuglen ske, når du definerer den. I dit tilfælde, for eksempel:
k=(~/code/public/*/*.launch) for i in "${k[@]}"; do
eller i det senere eksempel, du “skal eval
nogle af 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
- Bemærk hvordan
$exclude
indeholder jokertegn, du ‘ d har brug for at deaktivere globbing, før du bruger split + glob operatøren på den og gendanner den til$i/$j
og ikke brugeval
men brug"$i"/$j
- Både du og ilkkachu giver et godt svar. Men hans svar identificerede problemet. Så kredit til ham.
Svar
@ilkkachu svar løste det største globale problem. Fuld kredit til ham.
V1
På grund af exclude
, der indeholder poster både med og uden jokertegn (*), og de findes muligvis ikke alt i alt er der behov for ekstra kontrol efter globbing af $i/$j
. Jeg deler mine fund 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
Output Explaination
Følgende er den delvise output for at forklare situationen.
/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 ekskluderingsposten ($j
) har intet jokertegn, $i/$j
bliver en sammenkædning af almindelig streng. Filen / dir eksisterer dog 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 post ($j
) jokertegn, men har ingen fil- / katalogmatch, globbing af $i/$j
returnerer bare den originale streng.
V2
V2 brug et enkelt citat, eval
og shopt -s nullglob
for at få et rent resultat. Ingen endelig kontrol af fil / dir kræves.
#!/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
, globs i$exclude
kunne blive udvidet på tidspunktet for den$exclude
udvidelse (og opkaldeval
om det beder om problemer). Du ‘ vil have globbing aktiveret forfor i in $dir
ogfor l in $k
, men ikke tilfor j in $exclude
. Du ‘ vil have enset -f
før sidstnævnte og enset +f
til den anden. Mere generelt vil du ‘ gerne indstille din split + glob-operator, inden du bruger den. Under alle omstændigheder ønsker du ikke ‘ t split + glob tilecho $l
, så$l
skal citeres der. - @St é phaneChazelas henviser du til v1 eller v2? For v2 er både
exclude
ogdirs
i et enkelt citat (), so no globbing till
eval`. - Globbing finder sted ved ikke-citeret variabel udvidelse i listesammenhæng , at (at lade en variabel ikke citeres) er det vi kalder undertiden operatoren split + glob . Der er ‘ ingen globbing i tildelinger til skalarvariabler.
foo=*
ogfoo='*'
er det samme. Menecho $foo
ogecho "$foo"
er ikke (i skaller sombash
, det ‘ er rettet i skaller som zsh, fisk eller rc, se også linket ovenfor). Her gør du ønsker at bruge denne operatør, men nogle steder kun den delte del og andre kun glob-delen. - @St é phaneChazelas Tak for info !!! Tog mig engang, men jeg forstår bekymringen nu. Dette meget værdifuldt !! Tak !!!
Svar
Med zsh
:
exclude=" *.launch .classpath .sass-cache Thumbs.db ... " dirs=( ~/code/private/* ~/code/public/* ) for f ($^dirs/${^${=~exclude}}(N)) { echo $f }
${^array}string
skal udvides som $array[1]string $array[2]string...
. $=var
er at udføre ordopdeling på variablen (noget andre skaller gør som standard!), $~var
klodser på variablen (noget andet skaller også som standard (når du generelt ikke vil have dem til det, har du været nødt til at citere $f
ovenfor i andre skaller)).
(N)
er en glob-kvalifikator, der aktiverer nullglob for hver af disse globs, der er resultatet af den $^array1/$^array2
udvidelse. Det får globus til at udvide sig til ingenting, når de ikke matcher. Det sker også for at gøre en ikke-glob som ~/code/private/foo/Thumbs.db
til en, hvilket betyder, at hvis den pågældende ikke eksisterer, det er ikke inkluderet.
Kommentarer
- Dette er virkelig rart. Jeg testede og fungerer. Det ser dog ud til, at zsh er mere følsom over for newline, når ved hjælp af enkelt citat. Den måde,
exclude
er lukket på, påvirker output. - @JohnSiu, åh ja, du ‘ har ret. Det ser ud til, at split + glob og
$^array
skal udføres i to separate trin for at sikre, at de tomme elementer kasseres (se redigering). Det ligner lidt fejl izsh
, jeg ‘ Jeg rejser problemet på deres mailingliste. - Jeg kommer med en v2 til bash, som er renere, men stadig ikke så kompakt som dit zsh-script, lol
Svar
Gammel post, men jeg snublede ind her så t troede, at dette måske kan hjælpe andre 🙂
Der er en kinda-glob () -funktion i bash … den kaldes compgen -G Den udsender en værdi pr. linje så har brug for en readarray for at arbejde.
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 har brug for den sta y den måde indtil løkken. Kontroller min lange version.