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, in äquivalenter Weise ,
printf "%s\n" *snp* | wc -l
gibt alle Dateinamen aus, die snp
jeweils eine neue Zeile enthalten,
aber auch alle neuen Zeilen in den Dateinamen enthalten , und zählt dann die Anzahl der Zeilen in der Ausgabe. Wenn es eine Datei gibt, deren Name ist
f o o
s n p \n
b a r
. t s v
dann wird dieser Name als ausgeschrieben
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
Das zählt die Zeilen, die enthalten snp
, so dass das foosnp(\n)bar.tsv
Beispiel 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
wird Dateien enthalten, deren Namen mit beginnen .
; printf … *
Dies ist nicht der Fall, es sei denn, die dotglob
Shell-Option ist festgelegt.
printf
ist eine eingebaute Muschel; ls
ist ein externer Befehl. Daher ls
könnten die etwas mehr Ressourcen verbrauchen.
- Wenn die Shell a verarbeitet
*
, sortiert sie die Dateinamen.
ls -f
sortiert die Dateinamen nicht. Daher verbrauchen die ls
möglicherweise etwas weniger Ressourcen.
Aber sie haben etwas gemeinsam: Sie geben beide falsche Ergebnisse, wenn Dateinamen vorhanden sind, die Zeilenumbrüche enthalten und snp
sowohl vor als auch nach dem Zeilenumbruch stehen .
Ein weiterer:
filenamelist=(*snp*)
echo ${#filenamelist[@]}
Dadurch wird eine Shell-Array-Variable erstellt, in der alle enthaltenen Dateinamen aufgelistet snp
sind. Anschließend wird 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 sagten printf "%s\n" *snp*
, printf
wiederholte (wiederverwendete) der Befehl die "%s\n"
Formatzeichenfolge 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 "%.0s\n"
Formatzeichenfolge für jedes Argument in der Erweiterung von einmal wiederholt (wiederverwendet) *snp*
. Bedeutet "%.0s"
aber, die ersten Nullzeichen jeder Zeichenfolge zu drucken - dh nichts. Dieser printf
Befehl gibt nur eine neue Zeile (dh eine leere Zeile) für jede Datei aus, die snp
ihren Namen enthält . und dann wc -l
werden sie zählen. Und wieder können Sie die .
Dateien durch Festlegen einschließen dotglob
.