Chcę wiedzieć, ile zwykłych plików ma rozszerzenie .c
w dużej złożonej strukturze katalogów, a także w ilu katalogach te pliki są rozmieszczone. Dane wyjściowe, których chcę, to tylko te dwie liczby.
Widziałem to pytanie, jak uzyskać liczbę plików, ale muszę też znać liczbę katalogów, w których znajdują się pliki.
- Moje nazwy plików (w tym katalogi) mogą zawierać dowolne znaki; mogą zaczynać się od
.
lub-
i mieć spacje lub znaki nowej linii. - Mogę mieć pewne dowiązania symboliczne, których nazwy kończą się
.c
, i dowiązania symboliczne do katalogów. Nie chcę, aby dowiązania symboliczne były śledzone lub liczone, a przynajmniej chcę wiedzieć, czy i kiedy są one liczone. - Struktura katalogów ma wiele poziomów, a katalog najwyższego poziomu (katalog roboczy) zawiera co najmniej jeden
.c
plik.
Pośpiesznie napisałem kilka poleceń w powłoce (Bash), aby je policzyć, ale nie sądzę, aby wynik był dokładny ...
shopt -s dotglob
shopt -s globstar
mkdir out
for d in **/; do
find "$d" -maxdepth 1 -type f -name "*.c" >> out/$(basename "$d")
done
ls -1Aq out | wc -l
cat out/* | wc -l
Powoduje to wysyłanie skarg na niejednoznaczne przekierowania, pomijanie plików w bieżącym katalogu i wyzwalanie znaków specjalnych (na przykład przekierowane find
wyjście drukuje nowe wiersze w nazwach plików ) i zapisuje całą masę pustych plików (ups).
Jak w sposób niezawodny wyliczyć moje .c
pliki i zawierające je katalogi?
Jeśli to pomaga, oto kilka poleceń, aby utworzyć strukturę testową ze złymi nazwami i dowiązaniami symbolicznymi:
mkdir -p cfiles/{1..3}/{a..b} && cd cfiles
mkdir space\ d
touch -- i.c -.c bad\ .c 'terrible
.c' not-c .hidden.c
for d in space\ d 1 2 2/{a..b} 3/b; do cp -t "$d" -- *.c; done
ln -s 2 dirlink
ln -s 3/b/i.c filelink.c
W powstałej strukturze 7 katalogów zawiera .c
pliki, a 29 zwykłych plików kończy się na .c
(jeśli dotglob
jest wyłączone, gdy polecenia są uruchamiane) (jeśli się przeliczyłem, daj mi znać). To są liczby, których chcę.
Prosimy nie korzystać z tego konkretnego testu.
NB: Odpowiedzi w dowolnej powłoce lub innym języku będą przeze mnie testowane i doceniane. Jeśli będę musiał zainstalować nowe pakiety, nie ma problemu. Jeśli znasz rozwiązanie GUI, zachęcam do udostępnienia (ale nie mogę posunąć się tak daleko, aby zainstalować cały DE, aby go przetestować) :) Używam Ubuntu MATE 17.10.
Odpowiedzi:
Nie badałem danych wyjściowych za pomocą dowiązań symbolicznych, ale:
find
Polecenie wypisuje nazwy katalogów każdego.c
pliku znajdzie.sort | uniq -c
da nam liczbę plików w każdym katalogu (sort
może to być niepotrzebne tutaj, nie jestem pewien)sed
, zamieniam nazwę katalogu na1
, eliminując w ten sposób wszystkie możliwe dziwne znaki, tylko liczbę i1
pozostałetr
d
tutaj jest to w zasadzie to samo coNR
. Mógłbym pominąć wstawiając1
wsed
poleceniu, a tylko drukowaneNR
tutaj, ale myślę, że to jest nieco jaśniejsze.Do czasu
tr
dane są rozdzielane przez NUL, zabezpieczone przed wszystkimi prawidłowymi nazwami plików.Za pomocą Zsh i Bash można
printf %q
uzyskać ciąg cytowany, który nie zawierałby w nim nowych linii. Możesz być w stanie zrobić coś takiego:Jednakże, chociaż
**
nie powinno się rozszerzać dla dowiązań symbolicznych do katalogów , nie mogłem uzyskać pożądanego wyjścia na bash 4.4.18 (1) (Ubuntu 16.04).Ale zsh działało dobrze, a komendę można uprościć:
D
Umożliwia to glob, aby wybrać dot plików,.
wybiera zwykłe pliki (tak, nie symlinks) oraz:h
wydruki tylko ścieżkę do katalogu, a nie nazwa pliku (jakfind
„s%h
) (patrz rozdziały poświęcone Filename Generation i modyfikatorów ). Tak więc za pomocą polecenia awk musimy tylko policzyć liczbę unikalnych katalogów, a liczba wierszy to liczba plików.źródło
29 7
. Jeśli dodam-L
dofind
, to idzie w górę41 10
. Jakiej mocy potrzebujesz?Python ma
os.walk
, dzięki czemu takie zadania są łatwe, intuicyjne i automatycznie niezawodne nawet w obliczu dziwnych nazw plików, takich jak te, które zawierają znaki nowej linii. Ten skrypt Python 3, które pierwotnie opublikowany na czacie , jest przeznaczony do uruchomienia w bieżącym katalogu (ale to nie musi znajdować się w bieżącym katalogu, można zmienić to, co ścieżka przechodzi doos.walk
):Wyświetla liczbę katalogów, które bezpośrednio zawierają co najmniej jeden plik, którego nazwa kończy się na
.c
, następnie spację, a następnie liczbę plików, których nazwy kończą się na.c
. Pliki „ukryte” - to znaczy pliki, których nazwy zaczynają się od.
- są włączone, a katalogi ukryte są obsługiwane podobnie.os.walk
rekurencyjnie przechodzi przez hierarchię katalogów. Wymienia on wszystkie katalogi, które są rekurencyjnie dostępny od punktu początkowego ją podać, uzyskując informacje na temat każdego z nich jako krotki trzech wartościroot, dirs, files
. Dla każdego katalogu, do którego przechodzi, (w tym pierwszego, któremu nadasz nazwę):root
zawiera nazwę ścieżki tego katalogu. Należy pamiętać, że jest to całkowicie niezwiązane z „katalogu” systemu/
(a także niezwiązane/root
), chociaż to by iść do tych, jeśli zaczniesz tam. W takim przypadkuroot
zaczyna się od ścieżki.
- bieżący katalog - i idzie wszędzie pod nim.dirs
zawiera listę ścieżek wszystkich podkatalogów katalogu, w którym obecnie znajduje się nazwaroot
.files
zawiera listę ścieżek wszystkich plików znajdujących się w katalogu, w którym obecnie znajduje się nazwa,root
ale które same nie są katalogami. Zauważ, że obejmuje to inne rodzaje plików niż zwykłe pliki, w tym dowiązania symboliczne, ale wygląda na to, że nie spodziewasz się, że takie wpisy się kończą.c
i jesteś zainteresowany ich zobaczeniem.W takim przypadku muszę tylko zbadać trzeci element krotki
files
(który wywołujęfs
w skrypcie). Podobnie jakfind
polecenie, Pythonos.walk
przechodzi dla mnie do podkatalogów; jedyne, co muszę sprawdzić, to nazwy plików, które każdy z nich zawiera.find
Jednak w przeciwieństwie do polecenia,os.walk
automatycznie wyświetla mi listę tych nazw plików.Ten skrypt nie korzysta z dowiązań symbolicznych. Prawdopodobnie nie chcesz, aby dowiązania symboliczne były śledzone dla takiej operacji, ponieważ mogą one tworzyć cykle, a nawet jeśli nie ma cykli, te same pliki i katalogi mogą być przeglądane i liczone wiele razy, jeśli są dostępne poprzez różne dowiązania symboliczne.
Jeśli kiedykolwiek chciałeś
os.walk
podążać za dowiązaniami symbolicznymi - czego zwykle byś nie robił - możeszfollowlinks=true
to zrobić. Oznacza to, że zamiast pisaćos.walk('.')
można pisaćos.walk('.', followlinks=true)
. Powtarzam, że rzadko byś tego chciał, szczególnie w przypadku zadania takiego jak to, w którym rekurencyjnie wyliczasz całą strukturę katalogów, bez względu na to, jak duża jest, i zliczasz wszystkie pliki, które spełniają pewne wymagania.źródło
Znajdź + Perl:
Wyjaśnienie
find
Komenda znajdzie jakieś zwykłe pliki (więc nie ma dowiązania lub katalogi), a następnie wydrukować nazwę katalogu są w (%h
), a następnie\0
.perl -0 -ne
: czytaj wiersz po wierszu (-n
) i zastosuj skrypt podany przez-e
do każdego wiersza.-0
Ustawia separator linii wejściowej do\0
tak możemy przeczytać wejście null ograniczony.$k{$_}++
:$_
jest specjalną zmienną, która przyjmuje wartość bieżącego wiersza. Jest to używane jako klucz do skrótu%k
, którego wartości to liczba wyświetleń każdej linii wejściowej (nazwy katalogu).}{
: jest to skrótowy sposób pisaniaEND{}
. Wszelkie polecenia po nim}{
zostaną wykonane raz, po przetworzeniu wszystkich danych wejściowych.print scalar keys %k, " $.\n"
:keys %k
zwraca tablicę kluczy w haszu%k
.scalar keys %k
podaje liczbę elementów w tej tablicy, liczbę widocznych katalogów. Jest on drukowany wraz z bieżącą wartością$.
specjalnej zmiennej, która przechowuje bieżący numer linii wejściowej. Ponieważ jest to uruchamiane na końcu, bieżącym numerem linii wejściowej będzie numer ostatniej linii, a więc liczba linii widocznych do tej pory.Możesz rozwinąć polecenie perl do tego, dla przejrzystości:
źródło
Oto moja sugestia:
Ten krótki skrypt tworzy plik tymczasowy, znajduje każdy plik w bieżącym katalogu i kończy się na nim
.c
i zapisuje listę do pliku tymczasowego.grep
jest następnie używany do zliczania plików (następujące: Jak uzyskać liczbę plików w katalogu przy użyciu wiersza polecenia? ) dwa razy: Po raz drugi katalogi, które są wymienione wiele razy, są usuwane za pomocąsort -u
po usunięciu nazw plików z każdego wiersza za pomocąsed
.Działa to również poprawnie z nowymi
grep -c /
liniami w nazwach plików: zlicza tylko linie z ukośnikiem i dlatego uwzględnia tylko pierwszy wiersz nazwy pliku z wieloma liniami na liście.Wynik
źródło
Mały shellscript
Proponuję mały skrypt powłoki bash z dwoma głównymi wierszami poleceń (i zmienną,
filetype
aby ułatwić przełączanie w celu wyszukiwania innych typów plików).Nie szuka ani w dowiązaniach symbolicznych, tylko zwykłe pliki.
Pełny skrypt powłoki
Jest to bardziej szczegółowa wersja, która uwzględnia również dowiązania symboliczne,
Wyjście testowe
Z krótkiego skryptu:
Z pełnego shellscript:
źródło
Prosta wkładka Perl One:
Lub prościej za pomocą
find
polecenia:Jeśli lubisz grać w golfa i masz najnowszego (mniej niż dekadę) Perla:
źródło
Rozważ użycie
locate
polecenia, które jest znacznie szybsze niżfind
polecenie.Uruchamianie na danych testowych
Dzięki Muru za odpowiedź, która pomogła mi przez usunięcie symbolicznych linków z pliku Unixa i Linuksa .
Podziękowania dla Terdona za odpowiedź
$PWD
(nie skierowaną na mnie) w odpowiedzi na Unix i Linux .Oryginalna odpowiedź poniżej, do której odnoszą się komentarze
Skrócona forma:
sudo updatedb
Zaktualizuj bazę danych używaną przezlocate
polecenie, jeśli.c
pliki zostały utworzone dzisiaj lub jeśli pliki zostały usunięte.c
dzisiaj.locate -cr "$PWD.*\.c$"
zlokalizuj wszystkie.c
pliki w bieżącym katalogu i jego dzieci ($PWD
). Zamiast wypisywać nazwy plików i drukować liczbę z-c
argumentem. Wr
Określa regex zamiast domyślnego*pattern*
dopasowania, które mogą przynieść zbyt wiele wyników.locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l
. Znajdź wszystkie*.c
pliki w bieżącym katalogu i poniżej. Usuń nazwę pliku,sed
pozostawiając tylko nazwę katalogu. Policz liczbę plików w każdym katalogu za pomocąuniq -c
. Policz liczbę katalogów za pomocąwc -l
.Zacznij od bieżącego katalogu z jedną linią
Zauważ, jak zmieniła się liczba plików i liczba katalogów. Wierzę, że wszyscy użytkownicy mają
/usr/src
katalog i mogą uruchamiać powyższe polecenia z różną liczbą, w zależności od liczby zainstalowanych jąder.Długa forma:
Długa forma obejmuje czas, dzięki czemu można zobaczyć, ile czasu
locate
minęłofind
. Nawet jeśli musisz uruchomićsudo updatedb
, jest wiele razy szybszy niż jedenfind /
.Uwaga: To są wszystkie pliki na WSZYSTKICH dyskach i partycjach. tzn. możemy również wyszukiwać polecenia systemu Windows:
Mam automatycznie zamontowane trzy partycje NTFS systemu Windows 10
/etc/fstab
. Pamiętaj, że lokalizacje wiedzą wszystko!Ciekawa liczba:
Zliczenie 1 637 135 plików w 286 705 katalogach zajmuje 15 sekund. YMMV.
Aby
locate
zapoznać się ze szczegółowym podziałem obsługi wyrażeń regularnych poleceń (wydaje się, że nie jest to konieczne w tym pytaniu i odpowiedzi, ale jest używane na wszelki wypadek), przeczytaj to: Używać opcji „zlokalizuj” w określonym katalogu?Dodatkowe czytanie z ostatnich artykułów:
źródło
.c
(zwróć uwagę, że pęknie, jeśli-.c
w bieżącym katalogu jest plik o nazwie, ponieważ nie cytujesz*.c
), a następnie wydrukuje wszystkie katalogi w systemie, niezależnie od tego, czy zawierają pliki .c.~/my_c_progs/*.c
. Liczy 638 katalogów z.c
programami, całkowite katalogi są pokazane później jako286,705
. Poprawię odpowiedź na podwójny cytat „” .c ”. Dzięki za wskazówkę.locate -r "/path/to/dir/.*\.c$"
, ale nigdzie nie wspomniano w odpowiedzi. Podajesz tylko link do innej odpowiedzi, która o tym wspomina, ale bez wyjaśnienia, jak dostosować ją do odpowiedzi na zadane pytanie. Cała twoja odpowiedź skupia się na tym, jak policzyć całkowitą liczbę plików i katalogów w systemie, co nie ma znaczenia dla zadanego pytania, które brzmiało: „jak mogę policzyć liczbę plików .c i liczbę katalogów zawierających. c pliki w określonym katalogu ". Również twoje liczby są błędne, spróbuj na przykładzie w PO.$PWD
zmiennej: unix.stackexchange.com/a/188191/200094$PWD
nie zawiera znaków, które mogą być wyjątkowe w wyrażeniu regularnym