Informacja o systemie
System operacyjny: OS X
bash: GNU bash, wersja 3.2.57 (1) - wydanie (x86_64-apple-darwin16)
tło
Chcę, aby wehikuł czasu wykluczył zestaw katalogów i plików ze wszystkich moich projektów git / nodejs. Moi katalogów projektu są ~/code/private/
i ~/code/public/
tak staram się używać bash pętli wykonać tmutil
.
Kwestia
Krótka wersja
Jeśli mam obliczoną zmienną łańcuchową k
, jak sprawić, by była globalna w pętli for lub tuż przed nią:
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
W długiej wersji poniżej zobaczysz k=$i/$j
. Nie mogę więc zakodować ciągu w pętli for.
Długa wersja
#!/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
Wynik
Nie są globowane. Nie to, czego chcę.
~/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
bash
shell-script
quoting
wildcards
John Siu
źródło
źródło
k
jest ciągiem obliczonym i potrzebuję, aby został w pętli. Proszę sprawdzić moją długą wersję.Odpowiedzi:
Możesz wymusić kolejną rundę oceny
eval
, ale tak naprawdę nie jest to konieczne. (Ieval
zaczyna mieć poważne problemy, gdy nazwy plików zawierają znaki specjalne, takie jak$
.) Problem nie dotyczy globowania, ale rozszerzenia tyld.Globowanie następuje po rozwinięciu zmiennej, jeśli zmienna nie jest cytowana, jak tutaj (*) :
W tym samym duchu jest to podobne do tego, co zrobiłeś i działa:
Ale z tyldą nie:
Jest to wyraźnie udokumentowane dla Bash:
Rozwinięcie tyldy następuje przed rozwinięciem zmiennej, więc tyldy wewnątrz zmiennych nie są rozwijane. Łatwym obejściem jest użycie
$HOME
zamiast tego pełnej ścieżki.(* rozszerzanie globów ze zmiennych zwykle nie jest tym, czego chcesz)
Inna rzecz:
Kiedy zapętlasz wzory, jak tutaj:
zauważ, że jak
$exclude
to nie jest cytowane, jest zarówno podzielone, jak i globowane w tym momencie. Jeśli więc bieżący katalog zawiera coś pasującego do wzorca, zostanie rozwinięty do tego:Aby obejść ten problem, użyj zmiennej tablicowej zamiast podzielonego łańcucha:
Jako dodatkowy bonus wpisy tablicy mogą również zawierać białe znaki bez problemów z podziałem.
Można zrobić coś podobnego
find -path
, jeśli nie przeszkadza ci, na jakim poziomie katalogu powinny znajdować się pliki docelowe. Np. Aby znaleźć ścieżkę kończącą się na/e2e/*.js
:Musimy użyć
$HOME
zamiast~
tego samego powodu jak poprzednio, i$dirs
musi być nienotowane nafind
linii poleceń więc robi rozłam, ale$pattern
powinny być cytowane, więc nie jest przypadkowo rozszerzony przez powłokę.(Myślę, że możesz grać
-maxdepth
GNU find, aby ograniczyć zasięg wyszukiwania, jeśli cię to obchodzi, ale to trochę inna kwestia).źródło
find
? Właściwie też badam tę trasę, ponieważ pętla for staje się skomplikowana. Ale mam trudności z „-path”."${array[@]}"
(z cytatami!) jest udokumentowane (patrz tutaj i tutaj ), aby rozwinąć elementy jako odrębne słowa bez dalszego ich podziału.[abc]
jest standardową częścią globalnych wzorców , na przykład?
, nie sądzę, że konieczne jest omówienie ich wszystkich tutaj.Możesz zapisać go jako tablicę zamiast łańcucha, aby użyć go później w wielu przypadkach i pozwolić na tworzenie globowania podczas jego definiowania. W twoim przypadku na przykład:
lub w późniejszym przykładzie będziesz potrzebować
eval
niektórych ciągówźródło
$exclude
zawierają symbole wieloznaczne, musisz wyłączyć globowanie przed użyciem na nim operatora split + glob i przywrócić go do$i/$j
i nie używać,eval
ale używać"$i"/$j
Odpowiedź @ilkkachu rozwiązała główny problem globbing. Pełne uznanie dla niego.
V1
Jednak ze względu na to, że
exclude
zawiera wpisy zarówno z symbolem wieloznacznym, jak i bez niego (*), a także mogą one nie istnieć we wszystkich przypadkach, po dodaniu globbing konieczne jest dodatkowe sprawdzenie$i/$j
. Tutaj dzielę się swoimi odkryciami.Wyjaśnienie wyników
Poniżej znajduje się częściowy wynik wyjaśniający sytuację.
Powyższe są oczywiste.
Powyższe pojawia się, ponieważ exclude entry (
$j
) nie ma symboli wieloznacznych,$i/$j
staje się zwykłym łączeniem łańcuchów. Jednak plik / katalog nie istnieje.Powyższe wyświetlają się jako wykluczenie wpisu (
$j
) zawierają symbol wieloznaczny, ale nie pasuje do pliku / katalogu, globbing po$i/$j
prostu zwraca oryginalny ciąg.V2
V2 użyć pojedynczego cudzysłowu,
eval
ashopt -s nullglob
aby uzyskać czysty wynik. Nie wymaga sprawdzania końcowego pliku / katalogu.źródło
for j in $exclude
czasie globusy$exclude
mogą się rozszerzać w czasie tej$exclude
ekspansji (a wywołanieeval
tego wymaga kłopotów). Chcesz włączyć globowanie dlafor i in $dir
ifor l in $k
, ale nie dlafor j in $exclude
. Chciałbyśset -f
przed tym drugim iset +f
drugim. Mówiąc bardziej ogólnie, przed użyciem należy dostroić operatora split + glob. W każdym razie nie chcesz, aby split + glob byłecho $l
, więc$l
powinieneś się tam zacytować.exclude
idirs
są w pojedynczy cudzysłów (), so no globbing till
eval`.foo=*
ifoo='*'
jest taki sam. Aleecho $foo
iecho "$foo"
tak nie jest (w takich powłokachbash
, zostało to naprawione w powłokach takich jak zsh, fish lub rc, patrz także link powyżej). Tu nie chcą korzystać z tego operatora, ale w niektórych miejscach tylko część podziału, aw innych tylko część glob.Z
zsh
:${^array}string
jest rozwinąć jako$array[1]string $array[2]string...
.$=var
polega na dzieleniu słów na zmienną (coś, co inne powłoki domyślnie robią!),$~var
wykonuje globowanie na zmiennej (coś, co również inne powłoki domyślnie (gdy generalnie nie chcesz, żebyś musiał to zrobić cytat$f
powyżej) inne muszle)).(N)
to kwalifikator globu, który włącza nullglob dla każdej z globusów wynikających z tego$^array1/$^array2
rozszerzenia. To sprawia, że globusy rozszerzają się do zera, gdy się nie zgadzają. Zdarza się również, że zamienia nie-glob podobny~/code/private/foo/Thumbs.db
do jednego, co oznacza, że jeśli ten konkretny nie istnieje, nie zostanie uwzględniony.źródło
exclude
jest zamknięty, wpływa na wynik.$^array
należy to zrobić w dwóch osobnych krokach, aby upewnić się, że puste elementy zostaną odrzucone (patrz edycja). Wygląda to trochę jak błąd.zsh
Podniosę problem na ich liście mailingowej.