Jak uzyskać pojedynczą sumę wierszy z `wc -l`?

12

Dodałem alias git, aby podać mi liczbę wierszy określonych plików w mojej historii:

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

Jednak wc -lzgłasza wiele podsumowań, tak że jeśli mam więcej niż ~ 100 tys. Linii, zgłasza ich sumę, a następnie przechodzi do następnego etapu. Oto przykład:

<100 tys. Linii (pożądana wydajność)

$ 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

> 100 tys. Linii (do potoku grep "total")

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

Jak uzyskać prawdziwą sumę wc -l, a nie serię sum częściowych?

Ehryk
źródło
Według stackoverflow.com/questions/2501402/... problem tkwi w tym xargs, że nie wc. Nadal jestem zainteresowany tym, jak to naprawić i nie widzę dobrego rozwiązania w odpowiedziach.
Ehryk
3
Czy Twoja wersja wcobsługuje tę --files0-fromopcję? Potem możesz zrobić{ git ls-files -z ${1} | wc -l --files0-from=- ; }
Mark Plotnick
@MarkPlotnick Myślę, że zasługuje na odpowiedź.
terdon
Nie. wc: unrecognized option '--files0-from=-'
Ehryk

Odpowiedzi:

12

Spróbuj tego i przepraszamy za oczywistość:

cat *.cs | wc -l

lub z git:

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

Jeśli naprawdę chcesz, aby dane wyjściowe wyglądały jak wcdane wyjściowe, z indywidualnymi liczbami i sumą, możesz użyć awkdo zsumowania poszczególnych linii:

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

To nie będzie tak ładnie ustawione, jak wcto, w przypadku, gdy jest to dla Ciebie ważne. Aby to zrobić, musisz przeczytać cały zapis i zapisać go, obliczając sumę, a następnie użyć sumy do obliczenia szerokości pola przed użyciem tej szerokości do wydrukowania sformatowanego wyjścia zapamiętanych linii. Podobnie jak projekty remontów domowych, awkskrypty nigdy nie są tak naprawdę ukończone.

(Uwaga dla entuzjastycznych redaktorów: wyrażenie regularne w pierwszym awkwarunku występuje w przypadku, gdy istnieje plik, którego nazwa zaczyna się od „total” i spacji; w przeciwnym razie warunek mógłby być znacznie prostszy $2 == "total".)

rici
źródło
To działa, ale generuje tylko sumę całkowitą ( git ls-files -z ${1} | xargs -0 cat | wc -l). Brakuje mi jednak liczby wierszy na plik, jaką zapewnia wc -l, tak jak w moim pierwszym przykładzie powyżej. Jakiś sposób, aby uzyskać to, co najlepsze z obu światów tutaj?
Ehryk
Lub, jeśli jest to zbyt trudne, co powiesz na przełącznik, który mógłby go zepsuć: po prostu podaj sumę, jeśli nie, podaj normalne wc na plik z całkowitą mocą wyjściową?
Ehryk
@Ehryk: możesz to zrobić dwa razy, raz tak, jak to robiłeś, grep -vaby upuścić całkowitą liczbę wierszy, a raz tak, jak sugeruję, aby uzyskać całkowitą sumę. Możesz też wypróbować rozwiązanie awk w edytowanej odpowiedzi,
rici
+1: „Podobnie jak w przypadku projektów remontów domowych, skrypty awk nigdy nie są tak naprawdę ukończone”.
Ehryk
To działało jak urok. Mój końcowy wynik: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

Jeśli używasz Linuksa, wcprawdopodobnie pochodzi on z GNU Coreutils i ma --files0-fromopcję odczytu pliku (lub standardowego) zawierającego dowolnie długą listę nazw plików zakończonych przez NUL, które mają być policzone. Dokumentacja WC GNU Coreutils mówi: „Jest to przydatne, gdy lista nazw plików jest tak długa, że ​​może przekraczać ograniczenie długości wiersza poleceń. W takich przypadkach uruchomienie wc przez xargs jest niepożądane, ponieważ dzieli listę na części i powoduje wydrukowanie wc suma dla każdej podlisty zamiast dla całej listy ”.

Spróbuj tego:

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

Edycja: Ponieważ twoje wcpochodzi z ostatniego tysiąclecia i nie ma tej opcji, oto bardziej przenośne rozwiązanie, zakładając, że masz awki nie masz żadnych plików o nazwie „total”. Przefiltruje wynik wc, pomijając dowolne totalwiersze i zamiast tego sumując je i wypisując całkowitą sumę na końcu.

Nie wiem, czy gitimplementacja aliasu będzie miała problemy z pojedynczymi cudzysłowami $1i $2wewnątrz, które należy przekazać bez zmian 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
źródło
Nie korzystam z systemu Linux, jest on wyświetlany w wierszu polecenia git bash programu Git dla systemu Windows msysgit.github.io ( msysgit ).
Ehryk
DOBRZE. Więc xargsi wcty biegniesz z Cygwin? Czy możesz wkleić wynik wc --version?
Mark Plotnick
Nie pochodzą one z pełnej instalacji $ 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.
cygwina
Jest to pełny plik wykonywalny dla systemu Windows,C:\Program Files (x86)\Git\bin\wc.exe
Ehryk
@Ehryk Msysgit jest portem narzędzi Linuksa, ale zwykle ma stare wersje, więc może nie mieć --files0-from.
Gilles „SO- przestań być zły”
4

Problem polega na xargstym, że polecenie dzieli się na wiele przebiegów, więc wcraportuje sumę za każdym razem. Masz kilka opcji, możesz zachować rzeczy takimi, jakie są i przeanalizować dane wcwyjściowe:

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

Możesz cat plików:

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

Lub możesz xargscałkowicie pominąć (dostosowany tutaj ):

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

To się zepsuje, jeśli twoja lista plików jest dłuższa niż ARG_MAX .

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