it-swarm.com.de

Dateien im Verzeichnis mit einer bestimmten Zeichenfolge für den Namen zählen?

Ich habe folgende Dateien:

Codigo-0275_tdim.matches.tsv  
Codigo-0275_tdim.snps.tsv  
FloragenexTdim_haplotypes_SNp3filter17_single.tsv  
FloragenexTdim_haplotypes_SNp3filter17.tsv  
FloragenexTdim_SNP3Filter17.fas  
S134_tdim.alleles.tsv    
S134_tdim.snps.tsv  
S134_tdim.tags.tsv

Ich möchte die Anzahl der Dateien zählen, deren Name das Wort snp (Groß- und Kleinschreibung beachten) enthält. Ich habe es versucht

grep -a 'snp' | wc -l   

aber dann wurde mir klar, dass grep in den Dateien sucht. Was ist der richtige Befehl zum Durchsuchen der Dateinamen?

12
Lucia O

Meinen Sie damit, dass Sie in der Datei names nach snp suchen möchten? Das wäre ein einfacher Shell Glob (Platzhalter), der wie folgt verwendet wird:

ls -dq *snp* | wc -l

Lassen Sie das -q Flag, wenn Ihre Version von ls es nicht erkennt. Es behandelt Dateinamen mit "seltsamen" Zeichen (einschließlich Zeilenumbrüchen).

18
roaima

Wenn Sie ruhig in den Gängen von Unix & Linux stehen und genau zuhören, hören Sie eine gespenstische Stimme, die erbärmlich jammert: "Was ist mit Dateinamen, die Zeilenumbrüche enthalten?"

ls -d *snp* | wc -l

oder äquivalent ,

printf "%s\n" *snp* | wc -l

gibt alle Dateinamen aus, die snp enthalten, gefolgt von einer neuen Zeile aber auch alle neuen Zeilen in den Dateinamen und zählt dann die Anzahl der Zeilen in der Ausgabe. Wenn es eine Datei gibt, deren Name ist

 foosnp\nbar.tsv

dann wird dieser Name als geschrieben

foosnp
bar.tsv

was natürlich als zwei Zeilen gezählt wird.

Es gibt einige Alternativen, die zumindest in einigen Fällen besser sind:

printf "%s\n" * | grep -c snp

dies zählt die Zeilen, die snp enthalten, sodass das Beispiel foosnp(\n)bar.tsv von oben nur einmal zählt. Eine leichte Abweichung davon ist

ls -f | grep -c snp

Die beiden oben genannten Befehle unterscheiden sich darin:

  • Das ls -f Enthält Dateien, deren Namen mit . Beginnen. printf … * nicht, es sei denn, die Shell-Option dotglob ist gesetzt.
  • printf ist eine eingebaute Shell; ls ist ein externer Befehl. Daher verbraucht das ls möglicherweise etwas mehr Ressourcen.
  • Wenn die Shell einen * Verarbeitet, sortiert sie die Dateinamen. ls -f Sortiert die Dateinamen nicht. Daher verbraucht ls möglicherweise etwas weniger Ressourcen.

Aber sie haben etwas gemeinsam: Sie geben beide falsche Ergebnisse, wenn Dateinamen vorhanden sind, die newline enthalten und snp sowohl vor als auch nach der newline haben.

Ein anderer:

filenamelist=(*snp*)
echo ${#filenamelist[@]}

Dadurch wird eine Shell-Array-Variable erstellt, in der alle Dateinamen aufgelistet sind, die snp enthalten, und anschließend die Anzahl der Elemente im Array angegeben. Die Dateinamen werden als Zeichenfolgen und nicht als Zeilen behandelt, sodass eingebettete Zeilenumbrüche kein Problem darstellen. Es ist denkbar, dass dieser Ansatz ein Problem haben könnte, wenn das Verzeichnis sehr groß ist, da die Liste der Dateinamen im Shell-Speicher gespeichert werden muss.

Noch ein anderer:

Früher, als wir printf "%s\n" *snp* Sagten, wiederholte (wiederverwendete) der Befehl printf die Formatzeichenfolge "%s\n" Einmal für jedes Argument in der Erweiterung von *snp*. Hier nehmen wir eine kleine Änderung vor:

printf "%.0s\n" *snp* | wc -l

Dadurch wird die Formatzeichenfolge "%.0s\n" Für jedes Argument in der Erweiterung von *snp* Einmal wiederholt (wiederverwendet). Aber "%.0s" Bedeutet, die ersten Nullzeichen jeder Zeichenfolge zu drucken - d. H. Nichts. Dieser Befehl printf gibt nur eine neue Zeile (d. H. Eine leere Zeile) für jede Datei aus, deren Name snp enthält. und dann zählt wc -l sie. Und wieder können Sie die . - Dateien einschließen, indem Sie dotglob festlegen.

Abstrakt:

Funktioniert für Dateien mit "ungeraden" Namen (einschließlich neuer Zeilen).

set -- *snp* ; echo "$#"                             # change positional arguments

count=$(printf 'x%.0s' *snp*); echo "${#count}"      # most shells

printf -v count 'x%.0s' *snp*; echo "${#count}"      # bash

Beschreibung

Da ein einfacher Glob jedem Dateinamen mit snp in seinem Namen ein einfaches echo *snp* könnte für diesen Fall ausreichen, aber um wirklich zu zeigen, dass nur drei Dateien übereinstimmen, werde ich Folgendes verwenden:

$ ls -Q *snp*
"Codigo-0275_tdim.snps.tsv"  "foo * bar\tsnp baz.tsv"  "S134_tdim.snps.tsv"

Das einzige verbleibende Problem ist das Zählen der Dateien. Ja, grep ist eine übliche Lösung, und ja, das Zählen neuer Zeilen mit wc -l ist auch eine übliche Lösung. Beachten Sie, dass grep -c (count) zählt wirklich, wie oft eine snp Zeichenfolge übereinstimmt, und wenn ein Dateiname mehr als eine snp Zeichenfolge im Namen enthält, ist die Anzahl falsch.

Wir können es besser machen.

Eine einfache Lösung besteht darin, die Positionsargumente festzulegen:

$ set -- *snp*
$ echo "$#"
3

Um zu vermeiden, dass die Positionsargumente geändert werden, können wir jedes Argument in ein Zeichen umwandeln und die Länge der resultierenden Zeichenfolge drucken (für die meisten Shells):

$ printf 'x%.0s' *snp*
xxx

$ count=$(printf 'x%.0s' *snp*); echo "${#count}"
3

Oder, um eine Unterschale zu vermeiden:

$ printf -v count 'x%.0s' *snp*; echo "${#count}"
3

Dateiliste

Liste der Dateien (von der ursprünglichen Frage mit einer mit einem hinzugefügten Zeilenumbruch):

a='
Codigo-0275_tdim.matches.tsv
Codigo-0275_tdim.snps.tsv
FloragenexTdim_haplotypes_SNp3filter17_single.tsv
FloragenexTdim_haplotypes_SNp3filter17.tsv
FloragenexTdim_SNP3Filter17.fas
S134_tdim.alleles.tsv
S134_tdim.snps.tsv
S134_tdim.tags.tsv'
$ touch $a

touch $'foosnp\nbar.tsv' 

Das wird eine Datei mit einem Zeilenumbruch in der Mitte haben:

foosnp\nbar.tsv

Und um die Glob-Expansion zu testen:

$ touch $'foo * bar\tsnp baz.tsv'

Dadurch wird ein Sternchen hinzugefügt, das, wenn es nicht in Anführungszeichen gesetzt wird, auf die gesamte Liste der Dateien erweitert wird.

1
Isaac

angenommen, Sie wollten die Anzahl der HTML-Dateien zählen:

ls | grep ".html" | wc -l

wenn Sie also das Auftreten von "snp" zählen:

ls | grep "snp" | wc -l
0
Daniel McGrath