Wie kann ich Dateien mit einer bestimmten Erweiterung und den Verzeichnissen, in denen sie sich befinden, zählen?

14

Ich möchte wissen, wie viele reguläre Dateien die Erweiterung .cin einer großen komplexen Verzeichnisstruktur haben und wie viele Verzeichnisse sich auf diese Dateien verteilen. Die Ausgabe, die ich möchte, sind nur diese beiden Zahlen.

Ich habe diese Frage zum Abrufen der Anzahl der Dateien gesehen, muss aber auch die Anzahl der Verzeichnisse kennen, in denen sich die Dateien befinden.

  • Meine Dateinamen (einschließlich der Verzeichnisse) können beliebige Zeichen enthalten. Sie können mit .oder beginnen -und Leerzeichen oder Zeilenumbrüche enthalten.
  • Ich könnte einige Symlinks haben, deren Namen mit .cund Symlinks zu Verzeichnissen enden . Ich möchte nicht, dass Symlinks verfolgt oder gezählt werden, oder ich möchte zumindest wissen, ob und wann sie gezählt werden.
  • Die Verzeichnisstruktur besteht aus mehreren Ebenen und das oberste Verzeichnis (das Arbeitsverzeichnis) enthält mindestens eine .cDatei.

Ich habe einige Befehle hastig in die (Bash-) Shell geschrieben, um sie selbst zu zählen, aber ich glaube nicht, dass das Ergebnis korrekt ist ...

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

Dies gibt Beschwerden über mehrdeutige Weiterleitungen aus, verpasst Dateien im aktuellen Verzeichnis und stößt auf Sonderzeichen (bei einer umgeleiteten findAusgabe werden beispielsweise Zeilenumbrüche in Dateinamen gedruckt ) und schreibt eine ganze Reihe leerer Dateien (oops).

Wie kann ich meine .cDateien und die darin enthaltenen Verzeichnisse zuverlässig auflisten ?


Falls es hilft, sind hier einige Befehle zum Erstellen einer Teststruktur mit falschen Namen und Symlinks:

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

In der resultierenden Struktur enthalten 7 Verzeichnisse .cDateien und 29 reguläre Dateien enden mit .c(wenn dotglobdeaktiviert, wenn die Befehle ausgeführt werden) (wenn ich falsch gezählt habe, lassen Sie es mich bitte wissen). Das sind die Zahlen, die ich will.

Fühlen Sie sich bitte frei , diesen speziellen Test nicht zu verwenden.

NB: Antworten in einer beliebigen Shell oder einer anderen Sprache werden von mir getestet und geschätzt. Wenn ich neue Pakete installieren muss, kein Problem. Wenn Sie eine GUI-Lösung kennen, empfehle ich Ihnen, sie zu teilen (aber ich gehe möglicherweise nicht so weit, um eine ganze DE zu installieren, um sie zu testen) :) Ich verwende Ubuntu MATE 17.10.

Zanna
quelle
Das Schreiben eines Programms zum Umgang mit schlechten Programmiergewohnheiten stellte sich als ziemlich herausfordernd heraus;)
WinEunuuchs2Unix

Antworten:

16

Ich habe die Ausgabe nicht mit Symlinks untersucht, aber:

find . -type f -iname '*.c' -printf '%h\0' |
  sort -z |
  uniq -zc |
  sed -zr 's/([0-9]) .*/\1 1/' |
  tr '\0' '\n' |
  awk '{f += $1; d += $2} END {print f, d}'
  • Der findBefehl gibt den Verzeichnisnamen jeder gefundenen .cDatei aus.
  • sort | uniq -cwill gibt an, wie viele Dateien sich in jedem Verzeichnis befinden (das ist hier sortmöglicherweise unnötig, nicht sicher)
  • Mit sedersetze ich den Verzeichnisnamen durch 1und eliminiere so alle möglichen seltsamen Zeichen, nur die Anzahl und die 1verbleibenden
  • Damit kann ich mit in eine durch Zeilenumbrüche getrennte Ausgabe konvertieren tr
  • was ich dann mit awk zusammenfasse, um die Gesamtzahl der Dateien und die Anzahl der Verzeichnisse zu erhalten, die diese Dateien enthielten. Beachten Sie, dass dies dim Wesentlichen dasselbe ist wie NR. Ich hätte auf das Einfügen 1des sedBefehls verzichten und nur NRhier drucken können , aber ich denke, das ist etwas klarer.

Bis dahin trsind die Daten NUL-getrennt und gegen alle gültigen Dateinamen geschützt.


Mit zsh und bash können Sie printf %qeinen String in Anführungszeichen setzen, der keine Zeilenumbrüche enthält. Sie könnten also in der Lage sein, Folgendes zu tun:

shopt -s globstar dotglob nocaseglob
printf "%q\n" **/*.c | awk -F/ '{NF--; f++} !c[$0]++{d++} END {print f, d}'

Obwohl **Symlinks zu Verzeichnissen nicht erweitert werden sollen , konnte ich auf bash 4.4.18 (1) (Ubuntu 16.04) nicht die gewünschte Ausgabe erhalten.

$ shopt -s globstar dotglob nocaseglob
$ printf "%q\n" ./**/*.c | awk -F/ '{NF--; f++} !c[$0]++{d++} END {print f, d}'
34 15
$ echo $BASH_VERSION
4.4.18(1)-release

Aber zsh hat gut funktioniert und der Befehl kann vereinfacht werden:

$ printf "%q\n" ./**/*.c(D.:h) | awk '!c[$0]++ {d++} END {print NR, d}'
29 7

Daktiviert dieses Glob, um Punktdateien auszuwählen, .wählt reguläre Dateien (also keine Symlinks) aus und :hdruckt nur den Verzeichnispfad und nicht den Dateinamen (wie find's %h) (siehe Abschnitte über die Generierung von Dateinamen und Modifikatoren ). Mit dem Befehl awk müssen wir also nur die Anzahl der angezeigten eindeutigen Verzeichnisse und die Anzahl der Zeilen die Anzahl der Dateien zählen.

muru
quelle
Das ist großartig. Verwendet genau das, was benötigt wird und nicht mehr. Vielen Dank für den Unterricht :)
Zanna
@Zanna Wenn Sie einige Befehle veröffentlichen, um eine Verzeichnisstruktur mit Symlinks und die erwartete Ausgabe mit Symlinks wiederherzustellen, kann ich dies möglicherweise entsprechend korrigieren.
muru
Ich habe einige Befehle hinzugefügt, um eine (wie üblich unnötig komplizierte) Teststruktur mit Symlinks zu erstellen.
Zanna
@Zanna Ich denke, dieser Befehl muss nicht angepasst werden, um zu erhalten 29 7. Wenn ich hinzufügen -Lzu find, geht das bis zu 41 10. Welchen Output brauchst du?
muru
1
Es wurde eine zsh + awk-Methode hinzugefügt. Es gibt wahrscheinlich eine Möglichkeit, zsh dazu zu bringen, die Zählung für mich auszudrucken, aber keine Ahnung, wie.
muru
11

Python hat os.walk, was Aufgaben wie diese einfach, intuitiv und automatisch robust macht, selbst angesichts seltsamer Dateinamen wie denen, die Zeilenumbrüche enthalten. Dieses Python 3-Skript, das ich ursprünglich im Chat gepostet hatte , soll im aktuellen Verzeichnis ausgeführt werden ( es muss sich jedoch nicht im aktuellen Verzeichnis befinden und Sie können den Pfad ändern, an den es übergeben wird os.walk):

#!/usr/bin/env python3

import os

dc = fc = 0
for _, _, fs in os.walk('.'):
    c = sum(f.endswith('.c') for f in fs)
    if c:
        dc += 1
        fc += c
print(dc, fc)

Das gibt die Anzahl der Verzeichnisse aus, die direkt mindestens eine Datei enthalten, deren Name auf endet .c, gefolgt von einem Leerzeichen, gefolgt von der Anzahl der Dateien, deren Namen auf endet .c. "Versteckte" Dateien, .dh Dateien, deren Namen mit "-" beginnen, werden eingeschlossen, und versteckte Verzeichnisse werden auf ähnliche Weise durchsucht.

os.walkrekursiv durchläuft eine Verzeichnishierarchie. Es listet alle Verzeichnisse auf, auf die rekursiv von dem angegebenen Ausgangspunkt aus zugegriffen werden kann, und gibt Informationen zu jedem dieser Verzeichnisse als Tupel mit drei Werten aus root, dirs, files. Für jedes Verzeichnis, in das es übergeht (einschließlich des ersten Verzeichnisses, dessen Namen Sie ihm geben):

  • rootEnthält den Pfadnamen dieses Verzeichnisses. Beachten Sie, dass dies völlig unabhängig von dem System der „Wurzelverzeichnis“ /(und auch in keinem Zusammenhang mit /root) , obwohl es würde zu denen gehen , wenn Sie beginnen dort. In diesem Fall rootbeginnt der Pfad - .dh das aktuelle Verzeichnis - und verläuft überall darunter.
  • dirsEnthält eine Liste der Pfadnamen aller Unterverzeichnisse des Verzeichnisses, in dem sich der Name derzeit befindet root.
  • filesEnthält eine Liste der Pfadnamen aller Dateien , die sich in dem Verzeichnis befinden, in dem der Name derzeit gespeichert ist , die rootjedoch keine eigenen Verzeichnisse sind. Beachten Sie, dass dies andere Dateitypen als normale Dateien umfasst, einschließlich symbolischer Links. Es scheint jedoch, dass Sie nicht erwarten, dass solche Einträge auf enden, .cund dass Sie daran interessiert sind, welche zu sehen.

In diesem Fall muss ich nur das dritte Element des Tupels untersuchen files(das ich fsim Skript aufrufe). Wie der findBefehl wechselt Python os.walkfür mich in Unterverzeichnisse. Das einzige, was ich mir ansehen muss, sind die Namen der Dateien, die jede von ihnen enthält. Im Gegensatz zum findBefehl wird os.walkmir jedoch automatisch eine Liste dieser Dateinamen zur Verfügung gestellt.

Dieses Skript folgt keinen symbolischen Links. Sehr wahrscheinlich möchten Sie nicht, dass Symlinks für eine solche Operation befolgt werden, da sie Zyklen bilden können und auch wenn es keine Zyklen gibt, dieselben Dateien und Verzeichnisse möglicherweise mehrfach durchlaufen und gezählt werden, wenn auf sie über verschiedene Symlinks zugegriffen werden kann.

Wenn Sie os.walkSymlinks folgen wollten - was normalerweise nicht der Fall ist -, können Sie diese weiterleiten followlinks=true. Das heißt, anstatt zu schreiben, os.walk('.')könntest du schreiben os.walk('.', followlinks=true). Ich wiederhole, dass Sie das selten wollen, besonders bei einer Aufgabe wie dieser, bei der Sie eine ganze Verzeichnisstruktur rekursiv aufzählen, egal wie groß sie ist, und alle Dateien darin zählen, die eine bestimmte Anforderung erfüllen.

Eliah Kagan
quelle
7

Find + Perl:

$ find . -type f -iname '*.c' -printf '%h\0' | 
    perl -0 -ne '$k{$_}++; }{ print scalar keys %k, " $.\n" '
7 29

Erläuterung

Der findBefehl findet alle regulären Dateien (also keine Symlinks oder Verzeichnisse) und gibt dann den Namen des Verzeichnisses aus, in dem sie sich befinden ( %h), gefolgt von \0.

  • perl -0 -ne: Lies die Eingabe Zeile für Zeile ( -n) und wende das Skript von -eauf jede Zeile an. Das -0setzt das Eingabezeilentrennzeichen auf, \0damit durch Nullen getrennte Eingaben gelesen werden können.
  • $k{$_}++: $_ist eine spezielle Variable, die den Wert der aktuellen Zeile annimmt. Dies wird als Schlüssel für den Hash verwendet %k , dessen Werte die Häufigkeit angeben , mit der jede Eingabezeile (Verzeichnisname) gesehen wurde.
  • }{: Dies ist eine Kurzform des Schreibens END{}. Alle Befehle nach dem }{werden einmal ausgeführt, nachdem alle Eingaben verarbeitet wurden.
  • print scalar keys %k, " $.\n": keys %kgibt ein Array der Schlüssel im Hash zurück %k. scalar keys %kgibt die Anzahl der Elemente in diesem Array und die Anzahl der angezeigten Verzeichnisse an. Dies wird zusammen mit dem aktuellen Wert $.einer speziellen Variablen gedruckt , die die aktuelle Eingabezeilennummer enthält. Da dies am Ende ausgeführt wird, ist die aktuelle Eingabezeilennummer die Nummer der letzten Zeile, also die Anzahl der bisher gesehenen Zeilen.

Sie können den Perl-Befehl aus Gründen der Übersichtlichkeit folgendermaßen erweitern:

find  . -type f -iname '*.c' -printf '%h\0' | 
    perl -0 -e 'while($line = <STDIN>){
                    $dirs{$line}++; 
                    $tot++;
                } 
                $count = scalar keys %dirs; 
                print "$count $tot\n" '
terdon
quelle
4

Hier ist mein Vorschlag:

#!/bin/bash
tempfile=$(mktemp)
find -type f -name "*.c" -prune >$tempfile
grep -c / $tempfile
sed 's_[^/]*$__' $tempfile | sort -u | grep -c /

Dieses kurze Skript erstellt ein tempfile, findet jede Datei in und unter dem aktuellen Verzeichnis .cund schreibt die Liste in das tempfile. grepwird dann verwendet, um die Dateien zweimal zu zählen (wie folgt: Wie kann ich die Anzahl der Dateien in einem Verzeichnis über die Befehlszeile ermitteln? ): Beim zweiten Mal werden Verzeichnisse, die mehrfach aufgeführt sind, mit entfernt, sort -unachdem mit Dateinamen aus jeder Zeile entfernt wurden sed.

Dies funktioniert auch ordnungsgemäß mit Zeilenumbrüchen in Dateinamen: grep -c /Zählt nur Zeilen mit einem Schrägstrich und berücksichtigt daher nur die erste Zeile eines mehrzeiligen Dateinamens in der Liste.

Ausgabe

$ tree
.
├── 1
   ├── 1
      ├── test2.c
      └── test.c
   └── 2
       └── test.c
└── 2
    ├── 1
       └── test.c
    └── 2

$ tempfile=$(mktemp);find -type f -name "*.c" -prune >$tempfile;grep -c / $tempfile;sed 's_[^/]*$__' $tempfile | sort -u | grep -c /
4
3
Dessert
quelle
4

Kleines Muschelskript

Ich schlage ein kleines Bash-Shellscript mit zwei Hauptbefehlszeilen vor (und einer Variablen filetype, die das Umschalten erleichtert, um nach anderen Dateitypen zu suchen).

Es wird nicht nach oder in Symlinks gesucht, sondern nur nach regulären Dateien.

#!/bin/bash

filetype=c
#filetype=pdf

# count the 'filetype' files

find -type f -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l | tr '\n' ' '

# count directories containing 'filetype' files

find -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)'" \;|grep 'contains file(s)$'|wc -l

Ausführliches Shellscript

Dies ist eine ausführlichere Version, die auch symbolische Links berücksichtigt,

#!/bin/bash

filetype=c
#filetype=pdf

# counting the 'filetype' files

echo -n "number of $filetype files in the current directory tree: "
find -type f -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l

echo -n "number of $filetype symbolic links in the current directory tree: "
find -type l -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l
echo -n "number of $filetype normal files in the current directory tree: "
find -type f -name "*.$filetype" -ls|sed 's#.* \./##'|wc -l
echo -n "number of $filetype symbolic links in the current directory tree including linked directories: "
find -L -type f -name "*.$filetype" -ls 2> /tmp/c-counter |sed 's#.* \./##' | wc -l; cat /tmp/c-counter; rm /tmp/c-counter

# list directories with and without 'filetype' files (good for manual checking; comment away after test)
echo '---------- list directories:'
 find    -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)' || echo '{} empty'" \;
echo ''
#find -L -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)' || echo '{} empty'" \;

# count directories containing 'filetype' files

echo -n "number of directories with $filetype files: "
find -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)'" \;|grep 'contains file(s)$'|wc -l

# list and count directories including symbolic links, containing 'filetype' files
echo '---------- list all directories including symbolic links:'
find -L -type d -exec bash -c "ls -AF '{}' |grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)' || echo '{} empty'" \;
echo ''
echo -n "number of directories (including symbolic links) with $filetype files: "
find -L -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null && echo '{} contains file(s)'" \; 2>/dev/null |grep 'contains file(s)$'|wc -l

# count directories without 'filetype' files (good for checking; comment away after test)

echo -n "number of directories without $filetype files: "
find -type d -exec bash -c "ls -AF '{}'|grep -e '\.'${filetype}$ -e '\.'${filetype}'\*'$ > /dev/null || echo '{} empty'" \;|grep 'empty$'|wc -l

Ausgang testen

Aus dem kurzen Muschelskript:

$ ./ccntr 
29 7

Aus dem ausführlichen Shellscript:

$ LANG=C ./c-counter
number of c files in the current directory tree: 29
number of c symbolic links in the current directory tree: 1
number of c normal files in the current directory tree: 29
number of c symbolic links in the current directory tree including linked directories: 42
find: './cfiles/2/2': Too many levels of symbolic links
find: './cfiles/dirlink/2': Too many levels of symbolic links
---------- list directories:
. empty
./cfiles contains file(s)
./cfiles/2 contains file(s)
./cfiles/2/b contains file(s)
./cfiles/2/a contains file(s)
./cfiles/3 empty
./cfiles/3/b contains file(s)
./cfiles/3/a empty
./cfiles/1 contains file(s)
./cfiles/1/b empty
./cfiles/1/a empty
./cfiles/space d contains file(s)

number of directories with c files: 7
---------- list all directories including symbolic links:
. empty
./cfiles contains file(s)
./cfiles/2 contains file(s)
find: './cfiles/2/2': Too many levels of symbolic links
./cfiles/2/b contains file(s)
./cfiles/2/a contains file(s)
./cfiles/3 empty
./cfiles/3/b contains file(s)
./cfiles/3/a empty
./cfiles/dirlink empty
find: './cfiles/dirlink/2': Too many levels of symbolic links
./cfiles/dirlink/b contains file(s)
./cfiles/dirlink/a contains file(s)
./cfiles/1 contains file(s)
./cfiles/1/b empty
./cfiles/1/a empty
./cfiles/space d contains file(s)

number of directories (including symbolic links) with c files: 9
number of directories without c files: 5
$ 
Sudodus
quelle
4

Einfacher Perl One Liner:

perl -MFile::Find=find -le'find(sub{/\.c\z/ and -f and $c{$File::Find::dir}=++$c}, @ARGV); print 0 + keys %c, " $c"' dir1 dir2

Oder einfacher mit findBefehl:

find dir1 dir2 -type f -name '*.c' -printf '%h\0' | perl -l -0ne'$c{$_}=1}{print 0 + keys %c, " $."'

Wenn Sie gerne Golf spielen und aktuelles (weniger als zehn Jahre altes) Perl haben:

perl -MFile::Find=find -E'find(sub{/\.c$/&&-f&&($c{$File::Find::dir}=++$c)},".");say 0+keys%c," $c"'
find -type f -name '*.c' -printf '%h\0'|perl -0nE'$c{$_}=1}{say 0+keys%c," $."'
Hynek-Pichi-Vychodil
quelle
2

Verwenden Sie den locateBefehl, der viel schneller ist als der findBefehl.

Läuft auf Testdaten

$ sudo updatedb # necessary if files in focus were added `cron` daily.
$ printf "Number Files: " && locate -0r "$PWD.*\.c$" | xargs -0 -I{} sh -c 'test ! -L "$1" && echo "regular file"' _  {} | wc -l &&  printf "Number Dirs.: " && locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -cu | wc -l
Number Files: 29
Number Dirs.: 7

Vielen Dank an Muru für seine Antwort, die mir dabei hilft, symbolische Links aus der Dateizählung in Unix & Linux zu entfernen .

Danke an Terdon für die Antwort von $PWD(nicht an mich gerichtet) in Unix & Linux .


Ursprüngliche Antwort unten durch Kommentare verwiesen

Kurzform:

$ cd /
$ sudo updatedb
$ printf "Number Files: " && locate -cr "$PWD.*\.c$"
Number Files: 3523
$ printf "Number Dirs.: " && locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l 
Number Dirs.: 648
  • sudo updatedbAktualisieren Sie die vom locateBefehl verwendete Datenbank, wenn heute .cDateien erstellt wurden oder wenn Sie .cheute Dateien gelöscht haben.
  • locate -cr "$PWD.*\.c$"Suchen Sie alle .cDateien im aktuellen Verzeichnis und dessen untergeordneten Dateien ( $PWD). Anstatt Dateinamen auszudrucken, und zählen Sie mit -cArgument. Das rspezifiziert reguläre Ausdrücke anstelle der Standardübereinstimmung *pattern*, was zu vielen Ergebnissen führen kann.
  • locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l. Suchen Sie alle *.cDateien im aktuellen Verzeichnis und darunter. Entfernen Sie den Dateinamen, sedindem Sie nur den Verzeichnisnamen belassen. Zählen Sie die Anzahl der Dateien in jedem Verzeichnis mit uniq -c. Anzahl der Verzeichnisse mit zählen wc -l.

Beginnen Sie im aktuellen Verzeichnis mit einem Zeilenumbruch

$ cd /usr/src
$ printf "Number Files: " && locate -cr "$PWD.*\.c$" &&  printf "Number Dirs.: " && locate -r "$PWD.*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l
Number Files: 3430
Number Dirs.: 624

Beachten Sie, wie sich die Anzahl der Dateien und Verzeichnisse geändert hat. Ich glaube, dass alle Benutzer das /usr/srcVerzeichnis haben und die obigen Befehle mit unterschiedlicher Anzahl ausführen können, abhängig von der Anzahl der installierten Kernel.

Lange Form:

Die lange Form enthält die Zeit, sodass Sie sehen können, wie viel schneller vorbei locateist find. Auch wenn Sie laufen müssen sudo updatedb, ist es um ein Vielfaches schneller als ein einzelnes find /.

───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/Downloads$ sudo time updatedb
0.58user 1.32system 0:03.94elapsed 48%CPU (0avgtext+0avgdata 7568maxresident)k
48inputs+131920outputs (1major+3562minor)pagefaults 0swaps
───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/Downloads$ time (printf "Number Files: " && locate -cr $PWD".*\.c$")
Number Files: 3523

real    0m0.775s
user    0m0.766s
sys     0m0.012s
───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/Downloads$ time (printf "Number Dirs.: " && locate -r $PWD".*\.c$" | sed 's%/[^/]*$%/%' | uniq -c | wc -l) 
Number Dirs.: 648

real    0m0.778s
user    0m0.788s
sys     0m0.027s
───────────────────────────────────────────────────────────────────────────────────────────

Hinweis: Dies sind alle Dateien auf ALLEN Laufwerken und Partitionen. Dh wir können auch nach Windows Befehlen suchen:

$ time (printf "Number Files: " && locate *.exe -c)
Number Files: 6541

real    0m0.946s
user    0m0.761s
sys     0m0.060s
───────────────────────────────────────────────────────────────────────────────────────────
rick@alien:~/Downloads$ time (printf "Number Dirs.: " && locate *.exe | sed 's%/[^/]*$%/%' | uniq -c | wc -l) 
Number Dirs.: 3394

real    0m0.942s
user    0m0.803s
sys     0m0.092s

Ich habe drei Windows 10 NTFS-Partitionen automatisch eingehängt /etc/fstab. Seien Sie sich bewusst, locate weiß alles!

Interessante Anzahl:

$ time (printf "Number Files: " && locate / -c &&  printf "Number Dirs.: " && locate / | sed 's%/[^/]*$%/%' | uniq -c | wc -l)
Number Files: 1637135
Number Dirs.: 286705

real    0m15.460s
user    0m13.471s
sys     0m2.786s

Es dauert 15 Sekunden, um 1.637.135 Dateien in 286.705 Verzeichnissen zu zählen. YMMV.

Für eine detaillierte Aufschlüsselung locateder regulären Ausdrücke des Befehls (wird in dieser Frage und Antwort nicht benötigt, wird aber nur für den Fall verwendet) lesen Sie bitte Folgendes: Verwenden Sie "locate" unter einem bestimmten Verzeichnis?

Zusätzliche Lektüre aus neueren Artikeln:

WinEunuuchs2Unix
quelle
1
Dies zählt die Dateien in einem bestimmten Verzeichnis nicht. Wie Sie bereits erwähnt haben, werden alle Dateien (oder Verzeichnisse oder andere Dateitypen) gezählt, die übereinstimmen .c(beachten Sie, dass dies nicht funktioniert, wenn eine Datei -.cim aktuellen Verzeichnis enthalten ist, da Sie keine Anführungszeichen setzen *.c). Anschließend werden alle Verzeichnisse gedruckt im System, unabhängig davon, ob sie .c-Dateien enthalten.
Terdon
@terdon Sie können ein Verzeichnis übergeben ~/my_c_progs/*.c. Es werden 638 Verzeichnisse mit .cProgrammen gezählt, die Gesamtzahl der Verzeichnisse wird später als angezeigt 286,705. Ich werde die Antwort in Anführungszeichen "* .c" ändern. Danke für den Tipp.
WinEunuuchs2Unix
3
Ja, Sie können so etwas verwenden locate -r "/path/to/dir/.*\.c$", aber das wird in Ihrer Antwort nirgendwo erwähnt. Sie geben nur einen Link zu einer anderen Antwort, in der dies erwähnt wird, ohne jedoch zu erklären, wie Sie diese anpassen müssen, um die hier gestellte Frage zu beantworten. Ihre gesamte Antwort konzentriert sich darauf, wie die Gesamtzahl der Dateien und Verzeichnisse auf dem System gezählt wird, was für die Frage "Wie kann ich die Anzahl der .c-Dateien und die Anzahl der enthaltenen Verzeichnisse zählen?" Nicht relevant ist. c Dateien in einem bestimmten Verzeichnis ". Auch Ihre Nummern stimmen nicht, probieren Sie es am Beispiel im OP.
Terdon
@terdon Danke für deine Eingabe. Ich habe die Antwort mit Ihren Vorschlägen und einer Antwort verbessert, die Sie auf einer anderen SE-Site für $PWDVariable gepostet haben : unix.stackexchange.com/a/188191/200094
WinEunuuchs2Unix
1
Jetzt müssen Sie sicherstellen , dass $PWDkeine Zeichen enthält , die vielleicht besondere in einem regex
muru