2016-04-23 11 views
1

Ich habe ein kleines Skript zusammengestellt, das Dateien eines bestimmten Typs in einem Verzeichnis durchsuchen soll, in dem eindeutige Wortzahlen> 4 Zeichen gesammelt werden, aber es funktioniert nicht wie erwartet.Entfernen von Groß-/Kleinbuchstaben-Duplikaten in der Liste

  1. Es beseitigt nicht das gleiche Wort unabhängig vom Fall.
  2. Ich bin mir nicht sicher, wie man die Summen jedes Wortes offensichtlich zusammenzählt.
  3. Schließlich ist dies eine effiziente Möglichkeit, dies zu tun (wenn es tatsächlich funktioniert?).

Script:

#!/bin/bash 

file_list=() 
while IFS= read file ; do 
    file_list=("${file_list[@]}" "$file") 
    tr -sc 'A-Za-z' '\012' < "$file" | sort | uniq -c | egrep "\w{4,}" >> words.txt 
done < <(find . -maxdepth 1 -type f -name "*.c") 

# echo "${file_list[@]}" 

cat words.txt | sort -u | sort -nr 
echo "" > words.txt 

Beispiel Ausgabe:

38 char 
    35 return 
    25 static 
    18 year 
    18 char 
    10 COLS 
    10 CHAR 

Wie würde ich das übertölpelt Wort entfernen char im Beispiel oben, aber es ist Zahl in allen Dateien zu bekommen?

+1

Wie wird „Wort“ definiert? Ist es eine Zeichenkette, die durch ein Leerzeichen/Tab/eine neue Zeile oder etwas mehr getrennt ist? –

+0

@RanyAlbegWein: a-zA-Z (vier Zeichen +), getrennt durch Leerzeichen, Zeilenumbruch, Tabulator, ich denke, es deckt ab ... das ist, wofür ich mindestens gehe. danke –

Antwort

1

Konvertieren Sie zuerst in den Kleinbuchstaben als ersten Schritt in Ihrer Pipeline.

tr A-Z a-z <"$file" | tr -sc a-z '\012' | ... 

Zweitens, das Sortieren und statt der im Inneren der Schlaufe am Ende des Ganzen zu zählen:

... 
    tr A-Z a-z <"$file" | tr -sc a-z '\012' 
done < <(find ...) | sort | uniq -c | egrep "\w{4,}" >words.txt 
+0

Der erste Teil hilft wirklich, danke! –

+0

Der zweite Teil nicht? Es sollte das gewünschte Ergebnis haben, die Gesamtsummen über alle Dateien anzuzeigen. –

+0

Ich bin mir nicht ganz sicher, wie man das Finden Teil wie Ihr, so dass was fehlt. Ich habe den ersten Teil Ihrer Antwort mit dem Rest meines Codes wie folgt verwendet: https://gist.github.com/anonymous/a2c44d1fd67f70bf84ccb36fb5c3d651 –

0

Nachstehend verwendet Associative Arrays (Bash 4), um das Wort als Schlüssel zu speichern, und dessen Vorkommen als Wert:

declare -A arr 
while read -r word; do 
    arr[$word]=$((${arr[$word]} + 1)) 
done < <(find . -maxdepth 1 -type f -name '*.c' -exec grep -E '\w{4,}' {} \; | tr -s '[:space:]' \\n) 

Ja, es schneller durchführen können, aber Hinweis: Wenn Sie find ‚s \; Befehl ter ändern mination bis +, grep ergibt auch den Dateinamen als Teil der Ausgabe (was in unserem Fall der Schlüssel ist). Wir wollen diese Art von Verhalten nicht. Also, wenn Sie GNU grep haben - fügen Sie die -h Option neben find+ Befehlsterminierung hinzu.

von man grep zitiert:

-h, --no-filename 
      Suppress the prefixing of file names on output. This is the default when there is only one file (or only standard input) to search. 

d.h.:

find . -maxdepth 1 -type f -name '*.c' -exec grep -hE '\w{4,}' {} + | tr -s '[:space:]' \\n 

Zum Testen habe ich den folgenden Inhalt:

$ cat 1.c 2.c 
char return 
char char int 
char 
char switch  return 
int 
CHAR switch 
COLS 
year 
static 
char 
CHAR 
INT 
int 
main 
return case 
long 
double 

ich ein Skript sof namens erstellt, die der entsprechende Code oben plus eine declare -p arr enthält die zur Validierung Inhalt des assoziativen Arrays nach der Ausführung:

$ ./sof 
declare -A arr='([return]="3" [static]="1" [switch]="2" [int]="1" [CHAR]="2" [char]="6" [COLS]="1" [double]="1" [main]="1" [case]="1" [long]="1" [year]="1")' 

Es sieht gut aus, so können wir jetzt einfach ausdrucken nach Ihren Wunsch-Ausgabe:

$ for k in "${!arr[@]}";do v="${arr[$k]}"; printf '%s %s\n' "$v" "$k";done 
1 static 
3 return 
2 switch 
1 int 
6 char 
2 CHAR 
1 COLS 
1 main 
1 double 
1 case 
1 long 
1 year 
+0

Eine Sache, die möglicherweise nicht funktioniert, ist die Bash-Version, die zu spezifisch ist; Ich hoffe, dass dies auf Versionen 3.2+ funktioniert - danke –

+0

@NedSchneibly Es erfordert Bash 4+. Aber könnte für jemanden in der Zukunft nützlich sein. –

0

Alles, was Sie brauchen, ist:

awk -v RS='\\s' 'length()>3{cnt[tolower($0)]++} END{for (word in cnt) print cnt[word], word}' *.c 

Die oben verwendet GNU awk für Multi-char RS und \s, ist es eine einfache zwicken mit anderen awks:

awk '{for (i=1;i<=NF;i++) if (length($i)>3) cnt[tolower($i)]++} END{for (word in cnt) print cnt[word], word}' *.c 

wRT Ihre Frage zu stellen Ihre aktuelle Ansatz effizient ist - nein, es ist sehr ineffizient und läuft mindestens eine Größenordnung langsamer als das Skript, das ich oben gepostet habe. Lesen Sie why-is-using-a-shell-loop-to-process-text-considered-bad-practice.

Wenn Sie diese gefunden dann rekursiv für alle Dateien zu tun, könnte dies alles sein, das Sie brauchen:

awk -v RS='\\s' 'length()>3{cnt[tolower($0)]++} END{for (word in cnt) print cnt[word], word}' $(find -type f -name '*.c' -print) 

es sonst wird es tun:

find -type f -name '*.c' -print0 | 
xargs -0 cat | 
awk -v RS='\\s' 'length()>3{cnt[tolower($0)]++} END{for (word in cnt) print cnt[word], word}' 
+0

Müsste ich dies in meiner aktuellen Schleife verwenden, oder mit rekursiv finden? (Ich merke, dass ich 'max Tiefe 1 'hatte, obwohl ich denke, es zu erhöhen). Vielen Dank! –

+0

Ich habe meine Antwort bearbeitet, um das zu adressieren. –

+0

Hallo Ed, danke für das Update. Das Skript beginnt zu laufen, aber nach ein paar Sekunden bekomme ich einen Fehler: awk: kann den Wert von cnt nicht lesen; Es ist ein Array-Name. Eingabesatz Nummer 9648, Datei Quellzeilennummer 1'. –

Verwandte Themen