Wiem, że **/*.ext
rozwija 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?
Odpowiedzi:
To zadziała w Bash 4:
ls -l {,**/}*.ext
Aby glob z podwójną gwiazdką działał, globstar
opcja 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 **/*.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), a ls
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ęciu ls
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ść:
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 A
następuje po nim ls A/AB
itd., Więc A/AB
jest wyświetlany dwukrotnie. Ponadto ls
rozdzieli dane wyjściowe każdego podkatalogu:
...
<blank line>
directory name:
content-item
content-item
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 z find
. 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 z diff
było wyjście z katalogu bieżącego .
przez find
. Kiedy to zrobiłem, ls ** | wc -l
wynik miał prawie dwa razy więcej linii.
globstar
jest domyślnie ustawionyoff
**/*.ext
powinno wystarczyć. Poza tym nie będziesz mieć ukrytych plików, chyba że ty shopt -s dotglob
.
globstar
: shopt -u globstar
.
**/*.ext
nie wystarczy
Spowoduje to wydrukowanie wszystkich plików w bieżącym katalogu i jego podkatalogach, które kończą się na „.ext”.
find . -name '*.ext' -print
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 :
$ 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 mv
lub rm
i można przypadkowo usunąć niewłaściwe pliki.
**/*.*
) 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”.
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?
./{*,**/*}.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?