Informace o systému
OS: OS X
bash: GNU bash, verze 3.2.57 (1) -release (x86_64-apple-darwin16)
Pozadí
Chci, aby stroj času vyloučil ze všech mých projektů git / nodejs sadu adresářů a souborů. Adresáře mých projektů jsou v ~/code/private/
a ~/code/public/
, takže se snažím použijte smyčku bash k provedení tmutil
.
Vydání
Krátká verze
Pokud mám vypočítaná řetězcová proměnná k
, jak to udělám globálně nebo přímo před 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
V dlouhé verzi níže uvidíte k=$i/$j
. Řetězec tedy nemohu napevno smyčka for.
Dlouhá verze
#!/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
Výstup
Nejsou označeny globusy. To není to, co chci.
~/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
Komentáře
Odpovědět
Další kolo hodnocení můžete vynutit pomocí eval
, ale to ve skutečnosti není nutné. (A eval
začíná mít vážné problémy v okamžiku, kdy názvy souborů obsahují speciální znaky, jako je $
.) Problém není s globováním, ale s expanzí tildy.
Globbing se stane po rozšíření proměnné, pokud proměnná není uvedena, jako zde (*) :
$ x="/tm*" ; echo $x /tmp
Takže, ve stejném duchu je to podobné tomu, co jste udělali, a funguje to:
$ 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
Ale s vlnovkou to nedělá „t:
$ i="~/public/*"; j="*.launch"; k="$i/$j" $ echo $k ~/public/*/*.launch
Toto je jasně zdokumentováno pro Bash:
Pořadí expanzí je: expanze složené závorky; expanze vlnovky, expanze parametrů a proměnných, …
K expanzi vlnovky dochází před expanzí proměnné, takže tildy uvnitř proměnných se nerozbalí. Snadným řešením je použít $HOME
nebo úplnou cestu.
(* rozšíření globů z proměnných obvykle není to, co chcete)
Další věc:
Když procházíte smyčkami přes vzory, jako zde:
exclude="foo *bar" for j in $exclude ; do ...
Všimněte si, že protože $exclude
není uveden, je v tomto bodě rozdělen a také globlobován. Takže pokud aktuální adresář obsahuje něco, co odpovídá vzoru, je rozšířeno na toto:
$ 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
Chcete-li tento problém vyřešit, použijte místo děleného řetězce proměnnou pole / p>
$ 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
Jako další bonus mohou položky pole obsahovat i mezery bez problémů s rozdělením.
Něco podobného lze udělat s find -path
, pokud vám nevadí, na jaké úrovni adresáře by cílové soubory měly být. Například najít jakoukoli cestu končící /e2e/*.js
:
$ dirs="$HOME/public $HOME/private" $ pattern="*/e2e/*.js" $ find $dirs -path "$pattern" /home/foo/public/one/two/three/e2e/asdf.js
Musíme použít $HOME
místo ~
ze stejného důvodu jako dříve, a $dirs
je třeba uvést find
příkazový řádek, aby se rozdělil, ale $pattern
by měl být citován, aby nebyl omylem rozšířen shellem.
(Myslím, že byste mohli hrát s -maxdepth
na GNU find, abyste omezili hloubku hledání, pokud vám na tom záleží, ale to je trochu jiný problém.)
Komentáře
- Jste jedinou odpovědí s
find
? Vlastně také zkoumám tuto cestu, protože smyčka pro smyčku se komplikuje. Ale mám potíže s ‚ -path ‚. - Kredit vám jako informace o vlkodlaku ‚ ~ ‚ je více zaměřen na hlavní problém. Konečný scénář a vysvětlení zveřejním v jiné odpovědi. Ale plné uznání: D
- @JohnSiu, jo, používání find bylo to, co mi poprvé přišlo na mysl. Může to být také použitelné, v závislosti na přesné potřebě. (nebo ještě lépe, pro některá použití.)
- @kevinarpe, myslím, že pole jsou v podstatě určena právě pro toto, a ano,
"${array[@]}"
(s uvozovkami! ) je dokumentován (viz zde a zde ) k rozbalení na prvky jako odlišná slova bez dále je rozdělit. - @sixtyfive,
[abc]
je standardní součástí globálních vzorů , jako?
, nemyslím si ‚, že je ‚ nutné je zde všechny pokrýt .
Odpověď
Můžete jej uložit jako pole místo řetězce, který v mnoha případech použijete později, a nechte globování proběhnout, když jej definujete. Ve vašem případě například:
k=(~/code/public/*/*.launch) for i in "${k[@]}"; do
nebo v pozdějším příkladu „Bude potřeba eval
některé řetězce
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
Komentáře
- Všimněte si, jak
$exclude
obsahuje zástupné znaky, ‚ d je třeba před použitím operátoru split + glob na něm deaktivovat globbing a obnovit jej pro$i/$j
a nepoužívat použitíeval
ale použijte"$i"/$j
- Vy i ilkkachu dáváte dobrou odpověď. Jeho odpověď však problém identifikovala. Takže úvěr jemu.
Odpověď
Odpověď @ilkkachu vyřešila hlavní problém globalizace. Plná zásluha.
V1
Nicméně exclude
obsahující položky se zástupným znakem i bez něj (*) a také nemusí existovat Celkově je po globování $i/$j
zapotřebí další kontrola. Sdílím zde svá zjištění.
#!/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
Vysvětlení výstupu
Následuje částečný výstup vysvětlující situaci.
/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! ^^^
Výše uvedené jsou vysvětlující.
/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
Výše uvedené se zobrazí, protože položka vyloučení ($j
) nemá žádný zástupný znak, $i/$j
se stane prostým zřetězením řetězce. Soubor / adresář však neexistuje.
/Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.js /Volumes/HD2/JS/code/public/simple-api-example-ng2-express/e2e/*.map
Výše uvedené položky se zobrazují jako vyloučená položka ($j
) obsahují zástupný znak, ale nemá žádnou shodu souboru / adresáře, globbing $i/$j
stačí vrátit původní řetězec.
V2
V2 použít jednoduchou uvozovku, eval
a shopt -s nullglob
získáte čistý výsledek. Není nutná žádná konečná kontrola souboru / adresáře.
#!/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
Komentáře
- Jedním problémem je, že v
for j in $exclude
, globy v$exclude
se mohly v době této$exclude
expanze (a volání) rozšířiteval
o to žádá potíže). ‚ Chcete mít povoleno globování profor i in $dir
afor l in $k
, ale ne profor j in $exclude
. ‚ Chceteset -f
před druhou aset +f
pro druhou. Obecněji řečeno, ‚ byste chtěli vyladit operátor split + glob před jeho použitím. V každém případě ‚ nechcete split + glob proecho $l
, takže$l
by tam měl být citován. - @St é phaneChazelas, máte na mysli v1 nebo v2? U verze 2 jsou
exclude
adirs
uvedeny v jediné citaci (), so no globbing till
eval`. - Globování se odehrává při nekótované proměnné expanzi v kontextech seznamu , což (ponechání nekótované proměnné) je to, co někdy zavolat operátor split + glob . ‚ v přiřazeních ke skalárním proměnným nedochází k žádnému globbingu.
foo=*
afoo='*'
je stejný. Aleecho $foo
aecho "$foo"
nejsou (ve skořápkách jakobash
, ‚ bylo opraveno ve skořápkách jako zsh, fish nebo rc, viz také výše uvedený odkaz). Zde uděláte chci použít tento operátor, ale na některých místech pouze rozdělená část a na jiných pouze globální část. - @St é phaneChazelas Díky za informace !!! Vzal mě někdy, ale nyní chápu obavy. To je velmi cenné !! Děkuji !!!
Odpověď
S zsh
:
exclude=" *.launch .classpath .sass-cache Thumbs.db ... " dirs=( ~/code/private/* ~/code/public/* ) for f ($^dirs/${^${=~exclude}}(N)) { echo $f }
${^array}string
má expandovat jako $array[1]string $array[2]string...
. $=var
je rozdělení slov na proměnnou (ve výchozím nastavení to dělají i jiné skořápky!), $~var
provede na proměnné globování (něco jiného) granáty také ve výchozím nastavení (pokud je obvykle nechcete, musíte v jiných skořápkách citovat $f
výše)).
(N)
je kvalifikátor glob, který zapíná nullglob pro každý z globů vyplývající z $^array1/$^array2
expanze. To způsobí, že se koule globálně rozšíří na nic, když se neshodují. To také způsobí, že se z globálu podobného ~/code/private/foo/Thumbs.db
stane jeden, což znamená, že pokud ten konkrétní neexistuje, není to zahrnuto.
Komentáře
- To je opravdu pěkné. Testoval jsem a funguje. Zdá se však, že zsh je citlivější na nový řádek, když pomocí jednoduché uvozovky. Způsob, jakým je
exclude
uzavřen, ovlivňuje výstup. - @JohnSiu, ach ano, ‚ máte pravdu. Zdá se, že split + glob a
$^array
musí být provedeno ve dvou samostatných krocích, aby se zajistilo, že budou prázdné prvky zahozeny (viz úprava). To vypadá trochu jako chyba vzsh
, ‚ upozorním na problém v jejich e-mailovém seznamu. - Přijdu s v2 pro bash, který je čistší, ale stále není tak kompaktní jako váš skript zsh, lol
odpověď
starý příspěvek, ale narazil jsem sem, takže t myslel jsem, že to může pomoci ostatním 🙂
V bash je funkce kinda-glob () … nazývá se to compgen -G Vypisuje hodnotu na řádek, takže k práci potřebuje readarray.
Vyzkoušejte toto:
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
je vypočítaný řetězec a já ho potřebuji Tímto způsobem až do smyčky. Zkontrolujte moji dlouhou verzi.