2016-08-13 4 views
1

Ich möchte einen Bash-Befehl, der eine Tabelle zurückgibt, wobei jede Zeile die lesbare Dateigröße, die Anzahl der Zeilen und den Dateinamen enthält. Die Tabelle sollte nach Dateigröße sortiert sein.Von Menschen lesbare Dateigröße und Zeilenanzahl

Ich habe versucht, dies mit einer Kombination von du -hs, wc -l und sort -h und find zu tun.

Hier, wo ich bin:

find . -exec echo $(du -h {}) $(wc -l {}) \; | sort -h 
+0

Zeigen Sie uns, was Sie bekommen haben – eckes

+0

@eckes bearbeitet mit nicht funktionierendem Code – Hatshepsut

Antwort

1

Ihr Ansatz kurz nicht nur gefallen, weil die Shell Ihre Befehlsersetzungen erweitert ($(...)) vorne, aber im Grunde, weil können Sie nicht Shell Befehlszeilen übergeben direkt bis find:

find ‚s -exec Aktion kann nur aufrufen externe Dienstprogramme mit literal Argumente - die einzige nicht literal unterstützt Argument ist die {} die den Dateinamen (n) an die Hand.

choroba's answer behebt sofortige Ihr Problem durch eine separate Shell-Instanz bei jeder Iteration, der als Argument übergeben Zeichenfolge auszuführen, um die Shell-Befehl zum Aufrufen wird (-exec bash -c '...' \;).
Das funktioniert zwar (vorausgesetzt, Sie den {} Wert als Argument übergeben anstatt es in der Befehlszeilen-Zeichenfolge Einbettung), es ist auch ganz ineffizient, weil mehrere untergeordnete Prozessefür jeden Eingang erstellt werden Datei.

(Während es ein Weg ist find Pass (in der Regel), um alle Eingabedateien zu einem (in der Regel) einzigen Aufruf des angegebenen externen Dienstprogramm zu haben - nämlich mit Terminator + statt \;, dann ist dies nicht eine Option, hier aufgrund der Art der Befehlszeile übergeben.)

ein effiziente und robuste [1] Implementierung, die die Anzahl der Kind minimiert erstellt verarbeitet wie folgt aussehen würde:

Anmerkung: Ich gehe davon aus GNU Dienstprogramme hier durch Verwendung von head -n -1 und sort -h.
Auch ich bin Begrenzung find ‚s Ausgabe Dateien nur (wie bei den Verzeichnissen im Gegensatz), weil wc -l funktioniert nur auf Dateien.

paste <(find . -type f -exec du -h {} +) <(find . -type f -exec wc -l {} + | head -n -1) | 
    awk -F'\t *' 'BEGIN{OFS="\t"} {sub(" .+$", "", $3); print $1,$2,$3}' | 
    sort -h -t$'\t' -k1,1 
  • Beachten Sie die Verwendung von -exec ... + statt -exec ... \;, die das an einem einzigen Aufruf an das externe Programm übergeben typischerweise alle Eingabedateinamen zu sichern hat (wenn nicht alle Dateinamen passen auf eine einzige In der Befehlszeile werden Aufrufe effizient gebündelt, um möglichst wenige Anrufe zu tätigen.

  • wc -l {} + gibt immer eine Zusammenfassungszeile aus, die head -n -1 entfernt, aber auch Dateinamen nach jeder Zeilenanzahl ausgibt.

  • paste kombiniert die Zeilen jedes Befehls (deren jeweilige Eingaben von einer Prozesssubstitution stammen. <(...)) in einen einzelnen Ausgabestrom.

  • Der Befehl awk entfernt dann den überflüssigen Dateinamen, der von wc stammt, am Ende jeder Zeile.

  • Schließlich wird der Befehl sort sortiert das Ergebnis durch die 1. (-k1,1) laschen getrennt (-t$'\t') Säule durch Menschen lesbaren Zahlen (-h), wie die Zahlen, die du -h Ausgänge (z.B. 1K).


[1] Wie bei jeder Linie -orientierten Verarbeitung, Dateinamen mit eingebettetem newlines nicht unterstützt werden, aber ich halte dies nicht für eine reale Problem.

0

Das Problem ist, dass Shell die $(...) interpretiert, so find sie nicht bekommen. Sie zu entkommen, hilft auch nicht (\$\(du -h {}\)), da sie normale Parameter für die Befehle werden, nicht Befehlsersetzung.

Um sich als Kommandosubstitution zu interpretieren ist eine neue Shell zu nennen, entweder direkt

find . -exec bash -c 'echo $(du -h {}) $(wc -l {})' \; | sort -h 

oder durch ein Skript erstellen und von find aufrufen.

+0

Ist es möglich, den Dateinamen nur einmal anzuzeigen? – Hatshepsut

+1

@Hatshepsut: Sicher, benutze 'wc -l <{}'. – choroba

+0

Leider wird dies mit Dateinamen mit eingebetteten Metazeichen wie Leerzeichen (und möglicherweise mit eingebetteten Globbing-Zeichen) brechen. Um das zu beheben, müssten Sie 'finden. -exec bash -c "echo" $ (du -h "$ 1") $ (wc -l <"$ 1") "'- {} \; | sort -h' Obwohl dies das unmittelbare Problem des OP behebt und verführerisch kurz ist, führt dieser Ansatz mit großen Mengen von Eingabedateien nur schlecht aus, da er mehrere untergeordnete Prozesse _per Dateiname_ erstellt. – mklement0

1

Ok, ich habe es auch mit find/-exec versucht, aber die Flucht ist die Hölle. Mit einer Shell-Funktion funktioniert es ziemlich einfach:

#!/bin/bash 
function dir 
{ 
    du=$(du -sh "$1" | awk '{print $1}') 
    wc=$(wc -l < "$1") 
    printf "%10s %10s %s\n" $du $wc "${1#./}" 
} 

printf "%10s %10s %s\n" "size" "lines" "name" 
OIFS=$IFS; IFS="" 
find . -type f -print0 | while read -r -d $'\0' f; do dir "$f"; done 
IFS=$OIFS 

Mit basishm lesen Sie es ist sogar eine Art sicher mit Null Terminator. Das IFS wird benötigt, um zu vermeiden, dass das Lesen nachfolgende Leerzeichen in Dateinamen abschneidet.

BTW: $'\0' funktioniert nicht wirklich (das gleiche wie '') - aber es macht die Absicht klar.

Beispielausgabe:

 size  lines name 
     156K  708 sash 
     16K   64 hostname 
     120K  460 netstat 
     40K  110 fuser 
     644K  1555 dir/bash 
     28K   82 keyctl 
     2.3M  8067 vim 
Verwandte Themen