Co rekurencyjnie rozszerza się na wszystkie pliki w bieżącym katalogu?
92
Wiem, że **/*.extrozwija się do wszystkich plików we wszystkich pasujących podkatalogach *.ext, ale czym jest podobne rozszerzenie, które obejmuje również wszystkie takie pliki w bieżącym katalogu?
Mój bash nie daje rady **/*.ext. Czy na pewno to działa dla Ciebie?
tangens
@tangens Musisz włączyć globstaropcję zgodnie z odpowiedzią Dennisa.
kenorb
Odpowiedzi:
111
To zadziała w Bash 4:
ls -l {,**/}*.ext
Aby glob z podwójną gwiazdką działał, globstaropcja musi być ustawiona (domyślnie: włączona):
shopt -s globstar
Od man bash:
globstar
Jeśli jest ustawiona, wzorzec ** użyty w rozwinięciu nazw plików ogranicza
tekst będzie pasował do plików i zera lub większej liczby katalogów i
podkatalogach. Tylko jeśli po wzorze występuje /, tylko
katalogi i podkatalogi są zgodne.
Teraz zastanawiam się, czy kiedyś mógł wystąpić błąd w przetwarzaniu globstar, ponieważ teraz po prostu uzyskuję ls **/*.extpoprawne wyniki.
Niezależnie od tego spojrzałem na analizę, którą kenorb przeprowadził przy użyciu repozytorium VLC i znalazłem pewne problemy z tą analizą oraz w mojej odpowiedzi bezpośrednio powyżej:
Porównania z danymi wyjściowymi findpolecenia są nieprawidłowe, ponieważ określenie -type fnie obejmuje innych typów plików (w szczególności katalogów), a lswymienione polecenia prawdopodobnie tak. Ponadto jedno z wymienionych poleceń ls -1 {,**/}*.*- które wydaje się być oparte na moim powyżej, wyświetla tylko nazwy zawierające kropkę dla tych plików, które znajdują się w podkatalogach. Pytanie OP i moja odpowiedź zawierają kropkę, ponieważ poszukiwane są pliki z określonym rozszerzeniem.
Najważniejsze jest jednak to, że istnieje specjalny problem z używaniem lspolecenia ze wzorcem globstar **. Powstaje wiele duplikatów, ponieważ wzorzec jest rozszerzany przez Bash do wszystkich nazw plików (i nazw katalogów) w badanym drzewie. Po rozwinięciu lspolecenie wyświetla listę każdego z nich i ich zawartość, jeśli są katalogami.
Przykład:
W naszym aktualnym katalogu znajduje się podkatalog Ai jego zawartość:
A
└── AB
└── ABC
├── ABC1
├── ABC2
└── ABCD
└── ABCD1
Na tym drzewie **rozwija się do „AA / AB A / AB / ABC A / AB / ABC / ABC1 A / AB / ABC / ABC2 A / AB / ABC / ABCD A / AB / ABC / ABCD / ABCD1” (7 pozycji) . Jeśli to zrobisz echo **, otrzymasz dokładny wynik, a każdy wpis jest reprezentowany raz. Jeśli jednak to zrobisz ls **, wyświetli listę każdego z tych wpisów. Zasadniczo więc ls Anastępuje po nim ls A/ABitd., Więc A/ABjest wyświetlany dwukrotnie. Ponadto lsrozdzieli dane wyjściowe każdego podkatalogu:
Kiedyś trzamieniałem spacje na znaki nowej linii, co jest ważne tylko tutaj, ponieważ żadne nazwy nie zawierają spacji. Kiedyś sedusuwałem wiodący ./z każdego wiersza wyjścia z find. Posortowałem dane wyjściowe programu, findponieważ zwykle jest nieposortowane, a rozwinięcie globów przez Bash jest już posortowane. Jak widać, jedynym wyjściem z diffbyło wyjście z katalogu bieżącego .przez find. Kiedy to zrobiłem, ls ** | wc -lwynik miał prawie dwa razy więcej linii.
Przetestowałem Ubuntu i Cygwin i globstarjest domyślnie ustawionyoff
Steven Penny
12
Najlepsza odpowiedź! ale myślę, że **/*.extpowinno wystarczyć. Poza tym nie będziesz mieć ukrytych plików, chyba że ty shopt -s dotglob.
gniourf_gniourf
2
Aby wyłączyć globstar: shopt -u globstar.
kenorb
4
@gniourf_gniourf To pytanie w rzeczywistości prosi o uwzględnienie bieżącego katalogu, więc nie, **/*.extnie wystarczy
msciwoj
2
@dotnetCarpenter: Wersja Bash dostarczana z MacOS to 3.2, która, jak się dowiedziałeś, nie obsługuje globstar. Podwójna gwiazdka jest traktowana tak samo, jak pojedyncza. Globstar został wprowadzony w Bash 4.0.
Wstrzymano do odwołania.
13
Spowoduje to wydrukowanie wszystkich plików w bieżącym katalogu i jego podkatalogach, które kończą się na „.ext”.
Poniżej przedstawiamy testy innych odmian i ich zachowanie.
Folder testowy z 3472 plikami w przykładowym folderze repozytorium VLC :
(Razem pliki z 3472 liczonym według: find . -type f | wc -l)
ls -1 **/*.* - zwraca 3338
ls -1 {,**/}*.*- zwraca 3341 (zgodnie z propozycją Dennisa )
ls -1 {,**/}* - zwraca 8265
ls -1 **/*- zwraca 7817, z wyjątkiem plików ukrytych (zgodnie z propozycją Dennisa )
ls -1 **/{.[^.],}*- zwraca 7869 (zgodnie z propozycją Dennisa )
ls -1 {,**/}.?* - zwraca 15855
ls -1 {,**/}.* - zwraca 20321
Myślę więc, że najbliższą metodą rekurencyjnego wypisania wszystkich plików jest pierwszy przykład ( **/*.*) zgodnie z komentarzem gniourf-gniourf (zakładając, że pliki mają odpowiednie rozszerzenia lub używają tego konkretnego), ponieważ drugi przykład daje kilka więcej duplikatów, jak poniżej :
Aby dołączyć ukryte pliki, użyj: shopt -s dotglob(wyłącz przez shopt -u dotglob). Nie jest to zalecane, ponieważ może wpływać na polecenia takie jak mvlub rmi można przypadkowo usunąć niewłaściwe pliki.
Na terminalu Mac i bash z włączonym globstar znalazłem powyższe rozwiązanie ( **/*.*) jako pouczające i działające najlepiej. Zaakceptowana odpowiedź spowodowała duplikaty pozycji w głównym katalogu. Mój wzór pracy to:"${path}"**/*.*
mummybot
Byłoby interesujące wypróbowanie tego z innymi opcjami, takimi jak nullglob i dotglob
Wilf
4
$ find . -type f
Spowoduje to wyświetlenie wszystkich plików w bieżącym katalogu. Następnie możesz wykonać inne polecenie na wyjściu za pomocą -exec
$find . -type f -exec grep "foo" {} \;
Spowoduje to grepowanie każdego pliku z polecenia find dla ciągu „foo”.
Teraz, gdy minęło 11 lat, może nadszedł czas, aby ktoś wskazał, że find . -type fstosuje się rekurencyjnie z katalogiem głównym w bieżącym katalogu, a nie tylko do bieżącego katalogu.
Roger Dahl
4
Dlaczego po prostu nie użyć rozwinięcia nawiasów klamrowych, aby uwzględnić również bieżący katalog?
./{*,**/*}.ext
Ekspansja nawiasów ma miejsce przed rozszerzeniem glob, więc możesz skutecznie robić, co chcesz ze starszymi wersjami basha, i możesz zrezygnować z małpowania z globstar w nowszych wersjach.
Uważa się również, że dobrą praktyką w bash jest uwzględnienie wiodących ./we wzorcach globów.
**/*.ext
. Czy na pewno to działa dla Ciebie?globstar
opcję zgodnie z odpowiedzią Dennisa.Odpowiedzi:
To zadziała w Bash 4:
Aby glob z podwójną gwiazdką działał,
globstar
opcja musi być ustawiona (domyślnie: włączona):shopt -s globstar
Od
man bash
:Teraz zastanawiam się, czy kiedyś mógł wystąpić błąd w przetwarzaniu globstar, ponieważ teraz po prostu uzyskuję
ls **/*.ext
poprawne wyniki.Niezależnie od tego spojrzałem na analizę, którą kenorb przeprowadził przy użyciu repozytorium VLC i znalazłem pewne problemy z tą analizą oraz w mojej odpowiedzi bezpośrednio powyżej:
Porównania z danymi wyjściowymi
find
polecenia są nieprawidłowe, ponieważ określenie-type f
nie obejmuje innych typów plików (w szczególności katalogów), als
wymienione polecenia prawdopodobnie tak. Ponadto jedno z wymienionych poleceńls -1 {,**/}*.*
- które wydaje się być oparte na moim powyżej, wyświetla tylko nazwy zawierające kropkę dla tych plików, które znajdują się w podkatalogach. Pytanie OP i moja odpowiedź zawierają kropkę, ponieważ poszukiwane są pliki z określonym rozszerzeniem.Najważniejsze jest jednak to, że istnieje specjalny problem z używaniem
ls
polecenia ze wzorcem globstar**
. Powstaje wiele duplikatów, ponieważ wzorzec jest rozszerzany przez Bash do wszystkich nazw plików (i nazw katalogów) w badanym drzewie. Po rozwinięciuls
polecenie wyświetla listę każdego z nich i ich zawartość, jeśli są katalogami.Przykład:
W naszym aktualnym katalogu znajduje się podkatalog
A
i jego zawartość:Na tym drzewie
**
rozwija się do „AA / AB A / AB / ABC A / AB / ABC / ABC1 A / AB / ABC / ABC2 A / AB / ABC / ABCD A / AB / ABC / ABCD / ABCD1” (7 pozycji) . Jeśli to zrobiszecho **
, otrzymasz dokładny wynik, a każdy wpis jest reprezentowany raz. Jeśli jednak to zrobiszls **
, wyświetli listę każdego z tych wpisów. Zasadniczo więcls A
następuje po nimls A/AB
itd., WięcA/AB
jest wyświetlany dwukrotnie. Ponadtols
rozdzieli dane wyjściowe każdego podkatalogu:Tak więc użycie
wc -l
zlicza wszystkie te puste wiersze i nagłówki sekcji z nazwami katalogów, co jeszcze bardziej zmniejsza liczbę.To kolejny powód, dla którego nie powinieneś analizować
ls
.W wyniku dalszej analizy nie zalecam używania wzorca globstar w żadnym innym przypadku niż iterowanie po drzewie plików w ten sposób:
for entry in ** do something "$entry" done
Jako ostateczne porównanie użyłem repozytorium źródeł Bash, które miałem pod ręką i zrobiłem to:
shopt -s globstar dotglob diff <(echo ** | tr ' ' '\n') <(find . | sed 's|\./||' | sort) 0a1 > .
Kiedyś
tr
zamieniałem spacje na znaki nowej linii, co jest ważne tylko tutaj, ponieważ żadne nazwy nie zawierają spacji. Kiedyśsed
usuwałem wiodący./
z każdego wiersza wyjścia zfind
. Posortowałem dane wyjściowe programu,find
ponieważ zwykle jest nieposortowane, a rozwinięcie globów przez Bash jest już posortowane. Jak widać, jedynym wyjściem zdiff
było wyjście z katalogu bieżącego.
przezfind
. Kiedy to zrobiłem,ls ** | wc -l
wynik miał prawie dwa razy więcej linii.źródło
globstar
jest domyślnie ustawionyoff
**/*.ext
powinno wystarczyć. Poza tym nie będziesz mieć ukrytych plików, chyba że tyshopt -s dotglob
.globstar
:shopt -u globstar
.**/*.ext
nie wystarczySpowoduje to wydrukowanie wszystkich plików w bieżącym katalogu i jego podkatalogach, które kończą się na „.ext”.
find . -name '*.ext' -print
źródło
Możesz użyć:
**/*.*
aby dołączyć wszystkie pliki rekurencyjnie (włącz przez:)shopt -s globstar
.Poniżej przedstawiamy testy innych odmian i ich zachowanie.
Folder testowy z 3472 plikami w przykładowym folderze repozytorium VLC :
(Razem pliki z 3472 liczonym według:
find . -type f | wc -l
)ls -1 **/*.*
- zwraca 3338ls -1 {,**/}*.*
- zwraca 3341 (zgodnie z propozycją Dennisa )ls -1 {,**/}*
- zwraca 8265ls -1 **/*
- zwraca 7817, z wyjątkiem plików ukrytych (zgodnie z propozycją Dennisa )ls -1 **/{.[^.],}*
- zwraca 7869 (zgodnie z propozycją Dennisa )ls -1 {,**/}.?*
- zwraca 15855ls -1 {,**/}.*
- zwraca 20321Myślę więc, że najbliższą metodą rekurencyjnego wypisania wszystkich plików jest pierwszy przykład (
**/*.*
) zgodnie z komentarzem gniourf-gniourf (zakładając, że pliki mają odpowiednie rozszerzenia lub używają tego konkretnego), ponieważ drugi przykład daje kilka więcej duplikatów, jak poniżej :a drugi generuje kolejne duplikaty.
Aby dołączyć ukryte pliki, użyj:
shopt -s dotglob
(wyłącz przezshopt -u dotglob
). Nie jest to zalecane, ponieważ może wpływać na polecenia takie jakmv
lubrm
i można przypadkowo usunąć niewłaściwe pliki.źródło
**/*.*
) jako pouczające i działające najlepiej. Zaakceptowana odpowiedź spowodowała duplikaty pozycji w głównym katalogu. Mój wzór pracy to:"${path}"**/*.*
$ find . -type f
Spowoduje to wyświetlenie wszystkich plików w bieżącym katalogu. Następnie możesz wykonać inne polecenie na wyjściu za pomocą -exec
$find . -type f -exec grep "foo" {} \;
Spowoduje to grepowanie każdego pliku z polecenia find dla ciągu „foo”.
źródło
find . -type f
stosuje się rekurencyjnie z katalogiem głównym w bieżącym katalogu, a nie tylko do bieżącego katalogu.Dlaczego po prostu nie użyć rozwinięcia nawiasów klamrowych, aby uwzględnić również bieżący katalog?
Ekspansja nawiasów ma miejsce przed rozszerzeniem glob, więc możesz skutecznie robić, co chcesz ze starszymi wersjami basha, i możesz zrezygnować z małpowania z globstar w nowszych wersjach.
Uważa się również, że dobrą praktyką w bash jest uwzględnienie wiodących
./
we wzorcach globów.źródło