sed zählt Zeilen zwischen Mustern - mehrere Dateien

sed zählt Zeilen zwischen Mustern - mehrere Dateien

Ich habe mehrere .txtDateien in einem Verzeichnis. Jede Datei hat einen Abschnitt:

DONE
item 1
item 2
item 3
DONE

Ich möchte die Anzahl der Zeilen zwischen den beiden DONEMarkierungen für jede Datei separat zählen.

ich benutztediese Frageum dies zu erstellen:

sed -n "/DONE/,/DONE/ p" *.txt | wc -l > ~/word_count.txt

Dies kombiniert jedoch die Anzahlen für jede Datei zu einer Zahl. Stattdessen hätte ich gerne eine Ausgabe wie diese:

file1.txt 3
file2.txt 5
file3.txt 6

Antwort1

Besser verwenden awk, umzählen.

awk '
  FNR == 1 {inside = 0}
  $0 == "DONE" {
    if (inside) print FILENAME, n
    n = 0
    inside = ! inside
    next
  }
  inside {n++}' ./*.txt

Dadurch wird für jeden Abschnitt in jeder Datei ein Datensatz gedruckt DONE...DONE. Das bedeutet, dass nichts gedruckt wird, wenn es keinen solchen Abschnitt gibt. Um 0diese auszudrucken, benötigen Sie die GNU-Implementierung von awkmit ihren BEGINFILEspeziellen ENDFILEAnweisungen:

awk '
  BEGINFILE {DONE_count = 0}
  $0 == "DONE" {
    if (++DONE_count % 2 == 0) print FILENAME, n
    n = 0
    next
  }
  DONE_count % 2 {n++}
  ENDFILE {if (!DONE_count) print FILENAME, 0}' ./*.txt

Oder führen Sie einen awkpro Datei aus:

for file in ./*.txt; do
  awk '
    $0 == "DONE" {
      if (++DONE_count % 2 == 0) print FILENAME, n
      n = 0
      next
    }
    DONE_count % 2 {n++}
    END {if (!DONE_count) print FILENAME, 0}' "$file"
done

Antwort2

perl -lne '
   eof and !$a && print "$ARGV: ", 0+$a;          # no DONEs => ans=0
   next unless /DONE/ && !$a ... /DONE/;          # skip non-DONE ranges
   /DONE/ and !$a++ && next;                      # begin DONE range
   !/DONE/ and !eof and $a++,next;                # middle of DONE range
   !/DONE/ and eof and $a=2;                      # lone DONE => ans=0
   print "$ARGV: ", ($a-2, $a=0, close ARGV)[0];  # end of DONE range
                                                  # at the end we do 4 things: 1) subtract 2 from sum, 2) print filename+sum, 3) reset sum, and 4) skip the current file and jump to the next file in queue.
' ./*.txt

Mit sedkönnen wir dies für jede Datei einzeln tun:

for f in ./*.txt; do
   printf '%s: %d\n' "$f" "$(sed -e '/DONE/,/DONE/!d; //d' "$f" | wc -l)"
done

Der Unterschied besteht darin, dass wir keinen Abschluss durchführen lassen.

verwandte Informationen