Wie bekomme ich mit `wc -l` eine einzelne Gesamtsumme von Zeilen?

12

Ich habe einen Git-Alias ​​hinzugefügt, um die Zeilenanzahl bestimmter Dateien in meinem Verlauf zu ermitteln:

[alias]
lines = !lc() { git ls-files -z ${1} | xargs -0 wc -l; }; lc

Es werden jedoch wc -lmehrere Summen gemeldet, sodass, wenn ich mehr als ~ 100.000 Zeilen habe, die Gesamtsumme für diese gemeldet wird und dann fortgefahren wird. Hier ist ein Beispiel:

<100k Zeilen (gewünschte Ausgabe)

$ git lines \*.xslt
  46 packages/NUnit-2.5.10.11092/doc/files/Summary.xslt
 232 packages/NUnit-2.5.10.11092/samples/csharp/_UpgradeReport_Files/UpgradeReport.xslt
 278 total

> 100k Leitungen (mussten umleiten grep "total")

$ git lines \*.cs | grep "total"
 123569 total
 107700 total
 134796 total
 111411 total
  44600 total

Wie erhalte ich eine echte Summe wc -l, nicht eine Reihe von Zwischensummen?

Ehryk
quelle
Laut stackoverflow.com/questions/2501402/… liegt das Problem bei xargs, nicht wc. Ich bin immer noch daran interessiert, wie das Problem behoben werden kann, und ich sehe in den Antworten keine gute Lösung.
Ehryk
3
wcUnterstützt Ihre Version die --files0-fromOption? Dann können Sie{ git ls-files -z ${1} | wc -l --files0-from=- ; }
Mark Plotnick
@MarkPlotnick Ich denke, das verdient eine Antwort.
Terdon
Nee. wc: unrecognized option '--files0-from=-'
Ehryk

Antworten:

11

Versuchen Sie dies und entschuldigen Sie, dass Sie offensichtlich sind:

cat *.cs | wc -l

oder mit git:

git ls-files -z ${1} | xargs -0 cat | wc -l

Wenn die Ausgabe tatsächlich wie eine wcAusgabe mit einzelnen Zählern und einer Summe aussehen soll , können Sie awkdie einzelnen Zeilen folgendermaßen addieren:

git ls-files -z ${1} | xargs -0 wc -l |
awk '/^[[:space:]]*[[:digit:]]+[[:space:]]+total$/{next}
     {total+=$1;print}
     END {print total,"total"}'

Das wird nicht so gut aussehen wie wces ist, falls es Ihnen wichtig ist. Dazu müssen Sie die gesamte Eingabe lesen und speichern, die Gesamtsumme berechnen und dann die Gesamtsumme verwenden, um die Feldbreite zu berechnen, bevor Sie diese Feldbreite zum Drucken einer formatierten Ausgabe der gespeicherten Zeilen verwenden. Wie bei Renovierungsprojekten werden awkSkripte nie wirklich fertiggestellt.

(Hinweis für begeisterte Redakteure: Der reguläre Ausdruck in der ersten awkBedingung ist für den Fall, dass es eine Datei gibt, deren Name mit "total" und einem Leerzeichen beginnt. Andernfalls hätte die Bedingung viel einfacher sein können $2 == "total".)

rici
quelle
Das funktioniert, gibt aber nur die Summe aus ( git ls-files -z ${1} | xargs -0 cat | wc -l). Ich vermisse jedoch die Zeilenanzahl pro Datei, die wc -l wie in meinem ersten Beispiel oben bereitstellt. Wie kann man hier das Beste aus beiden Welten herausholen?
Ehryk
Oder, wenn das zu schwierig ist, wie wäre es mit einem Schalter, der das Ganze aufteilt? Wenn nicht, geben Sie einfach die Gesamtsumme an.
Ehryk
@Ehryk: Du könntest es einfach zweimal machen, einmal so, wie du es gemacht hast grep -v, um die Gesamtsumme zu löschen, und einmal so, wie ich es vorschlage, um die Gesamtsumme zu erhalten. Oder Sie könnten die awk-Lösung in der bearbeiteten Antwort versuchen,
rici
+1: "Wie bei Renovierungsprojekten werden awk-Skripte nie wirklich fertiggestellt."
Ehryk
Das hat wie ein Zauber gewirkt. Mein Endergebnis:git ls-files -z ${1} | xargs -0 wc -l | awk '/^[[:space:]]*[[:digit:]]+[[:space:]]+total$/{next} {total+=$1;print} END {print "\n Total:",total,"lines"}'
Ehryk
7

Wenn Sie Linux verwenden, wcstammt Ihr Betriebssystem wahrscheinlich von GNU Coreutils und hat die --files0-fromOption, eine Datei (oder stdin) zu lesen, die eine beliebig lange Liste von NUL-terminierten Dateinamen enthält, die gezählt werden sollen. In der GNU Coreutils wc-Dokumentation heißt es: "Dies ist nützlich, wenn die Liste der Dateinamen so lang ist, dass sie eine Beschränkung der Befehlszeilenlänge überschreitet. In solchen Fällen ist es unerwünscht, wc über xargs auszuführen, da die Liste in Teile aufgeteilt und wc gedruckt wird eine Summe für jede Unterliste und nicht für die gesamte Liste. "

Also versuche folgendes:

lc() { git ls-files -z ${1} | wc -l --files0-from=- ; } 

Bearbeiten: Da Sie wcaus dem letzten Jahrtausend stammen und diese Option nicht haben, ist dies eine portablere Lösung, vorausgesetzt, Sie haben awkund haben keine Dateien mit dem Namen "total". Es wird die Ausgabe von gefiltert wc, alle totalZeilen weggelassen und stattdessen summiert und die Gesamtsumme am Ende ausgedruckt.

Eine Sache, die ich nicht weiß, ist, ob die gitAlias-Implementierung Probleme mit den $1und $2in einfachen Anführungszeichen haben wird, die unverändert übergeben werden müssen awk.

lc() {
  git ls-files -z ${1} |
  xargs -0 wc -l |
  awk 'BEGIN { total=0; } { if (NF==2 && $2 == "total") total += $1; else print; } END { print total, "total"; }' ;
}
Mark Plotnick
quelle
Ich verwende kein Linux, es befindet sich in der Git-Bash-Eingabeaufforderung von Git für Windows msysgit.github.io (msysgit).
Ehryk
IN ORDNUNG. Also sind die xargsund wcdu rennst von Cygwin? Können Sie die Ausgabe von einfügen wc --version?
Mark Plotnick
Sie sind nicht von einer vollständigen Cygwin-Installation:$ wc --version wc (GNU textutils) 2.0 Written by Paul Rubin and David MacKenzie. Copyright (C) 1999 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Ehryk
Es ist voll auf Windows ausführbar,C:\Program Files (x86)\Git\bin\wc.exe
Ehryk
@Ehryk Msysgit ist eine Portierung der Linux-Tools, hat jedoch tendenziell ältere Versionen, so dass dies möglicherweise nicht der Fall ist --files0-from.
Gilles 'SO - hör auf, böse zu sein'
4

Das Problem besteht xargsdarin, dass der Befehl in mehrere Läufe aufgeteilt wcwird und die Gesamtsumme jedes Mal gemeldet wird. Sie haben ein paar Optionen, Sie können die Dinge so lassen, wie sie sind, und die wcAusgabe analysieren :

git ls-files -z ${1} | xargs -0 wc -l | awk '/total/{k+=$1}END{print k,"total"}';

Sie könnten die Dateien durchsuchen:

git ls-files -z ${1} | xargs -0 cat | wc -l

Oder Sie könnten xargsinsgesamt überspringen (von hier angepasst ):

unset files i; while IFS= read -r -d $'\0' name; do 
 files[i++]="$name"; 
done < <(git ls-files -z ${1} ) && wc -l "${files[@]}"

Das wird jedoch scheitern, wenn Ihre Dateiliste länger als ARG_MAX ist .

terdon
quelle
-1
j=0; for i in *.php *.js *.css; do let j+=`wc -l $i | awk {'print $1'}`; done; echo $j;
NilsonCain
quelle