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?

Ramon
źródło
4
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:

...
<blank line>
directory name:
content-item
content-item

Tak więc użycie wc -lzlicza 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ś 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.

Wstrzymano do odwołania.
źródło
5
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”.

find . -name '*.ext' -print
unutbu
źródło
Chociaż ta odpowiedź nie spełnia żądanego przez PO „rozszerzenia” w ścisłym tego słowa znaczeniu, najprawdopodobniej przyniesie pożądany rezultat.
Wstrzymano do odwołania.
7

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 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 :

$ diff -u <(ls -1 {,**/}*.*) <(ls -1 **/*.*)
--- /dev/fd/63  2015-04-19 15:25:07.000000000 +0100
+++ /dev/fd/62  2015-04-19 15:25:07.000000000 +0100
@@ -1,6 +1,4 @@
 COPYING.LIB
-COPYING.LIB
-Makefile.am
 Makefile.am
@@ -45,7 +43,6 @@
 compat/tdestroy.c
 compat/vasprintf.c
 configure.ac
-configure.ac

a drugi generuje kolejne duplikaty.


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.

kenorb
źródło
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”.

Amir Afghani
źródło
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.

clone206
źródło