staging.inyokaproject.org

Shell-Skript - Dateiendungen zählen

Status: Ungelöst | Ubuntu-Version: Ubuntu 19.04 (Disco Dingo)
Antworten |

Lollytrolly

Anmeldungsdatum:
4. Januar 2020

Beiträge: 2

Hallo

Ich möchte mir gern ein Shell-Skript schreiben, welches mir die häufigsten Dateinamensuffixen regulärer Dateien eines Verzeichnisses ausgibt bzw. zählt. Das habe ich auch soweit hinbekommen. Beispielsweise mit:

#!/bin/bash for i in find -maxdepth 1 -type f -name '*' | sed 's/.*\.\(.*\)$/\1/' | sort -uf ; do echo "$i: find -name \"*.$i\" | wc -l" done

Zu meiner Frage:

Wenn ich eine Datei OHNE Dateiendung habe, wird mir ein Slash vor genau diese Datei gehängt.

Ich möchte dennoch die Kategorie „no suffix“ zählen lassen.

Bsp: /dateiohneendung: 0

Wie kann ich sed so formulieren, dass es ein / am Anfang einer möglichen Datei (ohne Dateiendung, aber dafür mit diesem / am Anfang) löscht, WENN es ein solches Slash am Anfang des Dateinamens gibt? PS: Das Slash wurde von Linux selbst dort „plaziert“.

user_unknown

Avatar von user_unknown

Anmeldungsdatum:
10. August 2005

Beiträge: 17432

Lollytrolly schrieb:

Oh, Mist, ich wollte nur sehen, wie der Code im Editor aussieht. Weil er nicht als Code formatiert wurde ist er schlecht zu lesen. Dann kam ich aus Versehen auf "Veröffentlichen".

Also korrigiere ich mal das Layout:

1
2
3
4
#!/bin/bash
for i in `find -maxdepth 1 -type f -name '*' | sed 's/.*\.\(.*\)$/\1/' | sort -uf` ; do
        echo "$i: `find -name \"*.$i\" | wc -l`"
done

Backticks sind übrigens veraltet, schlecht lesbar, nicht portabel und schwer zu schachteln. Besser wäre:

1
2
3
4
5
#!/bin/bash
for i in $(find -maxdepth 1 -type f -name '*' | sed 's/.*\.\(.*\)$/\1/' | sort -uf)
do
        echo "$i: $(find -name "*.$i" | wc -l)"
done

Zu meiner Frage:

Wenn ich eine Datei OHNE Dateiendung habe, wird mir ein Slash vor genau diese Datei gehängt.

Und wenn Du mehrere hast?

Ich möchte dennoch die Kategorie „no suffix“ zählen lassen.

Bsp: /dateiohneendung: 0

Wie kann ich sed so formulieren, dass es ein / am Anfang einer möglichen Datei (ohne Dateiendung, aber dafür mit diesem / am Anfang) löscht, WENN es ein solches Slash am Anfang des Dateinamens gibt?

Es gibt nur zwei Zeichen, die nicht Teil eines Dateinamens sein können, die binäre 0 und der Slash.

PS: Das Slash wurde von Linux selbst dort „plaziert“.

Von find. Gnu-find auf Solaris oder BSD würde das wohl genauso machen. Mit printf kannst Du das führende ./ unterdrücken – da Du ja eh maxdepth auf 1 setzt versteht sich das von selbst:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11

for i in $(find -maxdepth 1 -type f -name "*" -printf "%f\n" | sed 's/.*\.\(.*\)$/\1/' | sort -uf); do   echo $i: $(find -name "*$i" | wc -l); done 
class: 39
dot: 4
html: 4
klein: 1
log: 1
noextension: 1
scala: 23
spec: 1
txt: 29

noextension ist eine Datei ohne Erweiterung.

seahawk1986

Anmeldungsdatum:
27. Oktober 2006

Beiträge: 10978

Lollytrolly schrieb:

Ich möchte mir gern ein Shell-Skript schreiben, welches mir die häufigsten Dateinamensuffixen regulärer Dateien eines Verzeichnisses ausgibt bzw. zählt.

Ich würde sowas ja mit Python machen:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#!/usr/bin/env python3
from collections import Counter
from pathlib import Path
import sys

def check_dir(d):
    try:
        directory = Path(d)
        if not directory.is_dir():
            raise ValueError(f"{d} is not a directory")
    except Exception as e:
        print(e, file=sys.stderr)
    else:
        c = Counter(f.suffix if f.suffix else "no suffix" for f in directory.glob('*') if f.is_file())
        print(f"Stats for {d}:")
        for key, num in c.most_common():
            print(f"{key:10}:{num:>5}")

for d in sys.argv[1:]:
    check_dir(d)

if not sys.argv[1:]:
    check_dir('.')

Mooi

Anmeldungsdatum:
15. August 2014

Beiträge: 187

Eigentlich reicht ein einziges find aus:

1
find -maxdepth 1 -type f -printf '%f\0' | awk 'BEGIN {RS="\0";FS="."} {if (NF==1) {a["no suffix"]++} else {a["."$NF]++}} END {for(i in a) print i":",a[i]}'

Es gäbe es übrigens noch mehr "Spezialfälle" zu regeln: Dateien ohne Suffix aber mit Punkt am Anfang und Dateien mit Punkt am Ende.

Lollytrolly

(Themenstarter)

Anmeldungsdatum:
4. Januar 2020

Beiträge: 2

WOW, sehr gut! Vielen lieben Dank!

Mooi: Besteht die Möglichkeit, dass du mir noch 1-2 kleinere Erläuterungen geben kannst, was genau im awk-Programm alles passiert bzw. welche Befehle dort genau stehen?

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 12527

Ich denke, es geht vielleicht noch einfacher:

1
find -type f | egrep -o '\.[^.]+$' | sort | uniq -c | sort -nr | head -20

rklm Team-Icon

Projektleitung

Anmeldungsdatum:
16. Oktober 2011

Beiträge: 12527

rklm schrieb:

1
find -type f | egrep -o '\.[^.]+$' | sort | uniq -c | sort -nr | head -20

Kleine Korrektur; so ist es besser:

1
find -type f | egrep -o '\.[^./]+$' | sort | uniq -c | sort -nr | head -20

Mit sed würde ich das so machen:

1
find -type f | sed -nr 's#^.*/[^./]+(\.[^./]+)$#\1#p' | sort | uniq -c | sort -nr | head -20

Man kann noch ein tr 'a-z' 'A-Z' | for den ersten sort setzen um die Sache unabhängig von Groß- und Kleinschreibung zu machen.

Mooi

Anmeldungsdatum:
15. August 2014

Beiträge: 187

Lollytrolly schrieb:

Mooi: Besteht die Möglichkeit, dass du mir noch 1-2 kleinere Erläuterungen geben kannst, was genau im awk-Programm alles passiert bzw. welche Befehle dort genau stehen?

Die Dateinamen werden einzeln in Felder aufgeteilt, getrennt von Punkten. Vom jeweils letzten Feld wird angenommen, das es eine Suffix ist. Damit wird das assoziative Array a[] gebildet, das als Adressen die Suffixen enthält, oder den Text "no suffix" (wenn nur ein Feld existiert - also kein Punkt). Die jeweils zugehörigen Werte werden hochgezählt (++) und entsprechen so der Anzahl der Vorkommen.

Enthält ein untersuchtes Dir also z.B. 1 Gif, 2 Jpg und 3 Dateien ohne Suffix, entsteht folgendes Array:

a[.gif] == 1
a[.jpg] == 2
a[no suffix] == 3

Die For-Schleife im END-Block gibt das Array dann für das Auge aufbereitet aus.

Antworten |