Zählen Sie die Zeilenlängen in der Datei mit den Befehlszeilentools

72

Problem

Wie kann ich die Vorkommen jeder Zeilenlänge zählen, wenn ich eine lange Datei mit vielen Zeilen unterschiedlicher Länge habe?

Beispiel:

file.txt

this
is
a
sample
file
with
several
lines
of
varying
length

Laufen count_line_lengths file.txtwürde geben:

Length Occurences
1      1
2      2
4      3
5      1
6      2
7      2

Ideen?

Pete Hamilton
quelle
Woher weißt du, length=1für welches Wort? Sie sollten das Wort auch speichern.
Bill
Sprache: Verwenden Sie vorzugsweise einen cleveren Shell-Befehl. Ich könnte das leicht in so etwas wie Ruby oder Python machen, aber das macht keinen Spaß;)
Pete Hamilton
@ Bill Das Wort interessiert mich nicht wirklich, nur die Zeilenlängen, es sei denn, ich habe deine Frage falsch verstanden?
Pete Hamilton

Antworten:

103

count.awk:

{
  print length($0);
}

...

$ awk -f count.awk input.txt | sort | uniq -c
      1 1
      2 2
      3 4
      1 5
      2 6
      2 7
Ignacio Vazquez-Abrams
quelle
85
Oder kürzer:awk '{print length}' input.txt | sort | uniq -c
Anders Johansson
1
Schöne Pfeifenschlange, aber Zählen und leicht uniqdrinnen awk. Ich nehme an, die Sortierung kann auch in erfolgen gawk. Ich bevorzuge die reine bashLösung.
TrueY
10
Ich habe das getan, aber wir haben wirklich lange Zeilen und die Sortierung sortiert die Zahlen standardmäßig nicht richtig (ich habe eine Ausgabe wie folgt erhalten 1 9575 1 999. Um die Zahlen richtig zu sortieren sort -g, verwenden Sie das Originalawk '{print length}' input.txt | sort -g | uniq -c
user82116
@ user82116 Ich glaube, das Ersetzen dieses sortBefehls durch LC_ALL=C sorthätte den Vorteil, dass die Zeichen auch richtig sortiert und schneller sind.
Prometheus
28

Pure awk

awk '{++a[length()]} END{for (i in a) print i, a[i]}' file.txt

4 3
5 1
6 2
7 2
1 1
2 2
iruvar
quelle
10

Verwenden von bashArrays:

#!/bin/bash

while read line; do
    ((histogram[${#line}]++))
done < file.txt

echo "Length Occurrence"
for length in "${!histogram[@]}"; do
    printf "%-6s %s\n" "${length}" "${histogram[$length]}"
done

Beispiellauf:

$ ./t.sh
Length Occurrence
1      1
2      2
4      3
5      1
6      2
7      2
Adrian Frühwirth
quelle
1
@fedorqui Es ist allerdings nicht wirklich portabel, also awkgewinnt es je nach Anwendungsfall ;-) Habe es gerade gepostet, weil das OP speziell nach etwas gefragt hat, das keine andere externe Sprache beinhaltet, was auch bedeutet awk(so habe ich es gelesen). Auf der anderen Seite ist es nicht einmal mehr so ​​lange, wenn man bedenkt while read l;do((h[${#l}]++));done<file.txt;for l in "${!h[@]}";do echo "$l ${h[$l]}";done...
Adrian Frühwirth
8
$ perl -lne '$c{length($_)}++ }{ print qq($_ $c{$_}) for (keys %c);' file.txt

Ausgabe

6 2
1 1
4 3
7 2
2 2
5 1
jfs
quelle
2
für perl -lnE '$c{+length}++}{say "$_ $c{$_}" for keys %c'
Golfspaß
3
Ich hatte eine Datei mit einer pathologisch langen Zeile (700-1000 MB) und von allen Einzeilern hier stürzte nur dieser nicht ab. +1!
Randall Cook
1

Sie können dies erreichen, indem Sie nur grundlegende Unix-Dienstprogramme verwenden:

$ printf "% s% s \ n" $ (für Zeile in $ (cat file.txt); do printf $ line | wc -c; done | sort -n | uniq -c | sed -E "s / ([ 0-9] +) [^ 0-9] + ([0-9] +) / \ 2 \ 1 / ")
1 1
2 2
4 3
5 1
6 2
7 2

Wie es funktioniert?

  1. Hier ist die Quelldatei:
    $ cat file.txt
    diese
    ist
    ein
    Stichprobe
    Datei
    mit
    mehrere
    Linien
    von
    variieren
    Länge
    
  2. Ersetzen Sie jede Zeile der Quelldatei durch ihre Länge:
    $ für Zeile in $ ( cat file.txt ); printf $ line | wc -c; getan
    4
    2
    1
    6
    4
    4
    7
    5
    2
    7
    6
    
  3. Sortieren und zählen Sie die Anzahl der Längenvorkommen:
    $ für Zeile in $ (cat file.txt); printf $ line | wc -c; erledigt | sort -n | uniq -c
          1 1
          2 2
          3 4
          15
          2 6
          2 7
    
  4. Tauschen und formatieren Sie die Zahlen:
    $ printf "% s% s \ n" $ ( für Zeile in $ (cat file.txt); do printf $ line | wc -c; done | sort -n | uniq -c | sed -E "s / ([ 0-9] +) [^ 0-9] + ([0-9] +) / \ 2 \ 1 / ") 
    1 1
    2 2
    4 3
    5 1
    6 2
    7 2
    
Maksym Ganenko
quelle
2
wc -czählt die Bytes , nicht die Zeichen. Wenn Sie Multibyte-Zeichen haben, erhalten Sie größere Zahlen. Versuchen Sie es mit echo -n "你好" | wc -c`echo -n" 你好 "| wc -m`.
Imrek
@DrunkenMaster Sie richtig sein muss, sollte ich einfach ersetzen wc -cmit wc -m?
Maksym Ganenko
1
Ich denke, es wird jetzt für jeden klar sein, der Ihre Antwort liest. Es reicht aus, auf den obigen Kommentar zu verweisen.
Imrek
1

Wenn Sie zulassen, dass die Spalten ausgetauscht werden und die Überschriften nicht benötigt werden, ist dies so einfach wie

while read line; do echo -n $line | wc -m; done < file | sort | uniq -c

(ohne fortgeschrittene Tricks mit sedoder awk) wird funktionieren. Die Ausgabe ist:

1 1
2 2
3 4
1 5
2 6
2 7

Eine wichtige Sache, die Sie beachten sollten: wc -cZählt die Bytes, nicht die Zeichen, und gibt nicht die richtige Länge für Zeichenfolgen an, die Multibyte-Zeichen enthalten. Daher die Verwendung von wc -m.

Verweise:

Mann uniq (1)

Mannsorte (1)

Mann wc (1)

imrek
quelle