Bagaimana cara mendapatkan total baris tunggal dengan `wc -l`?

12

Saya telah menambahkan alias git untuk memberi saya jumlah baris file tertentu dalam riwayat saya:

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

Namun, wc -lmelaporkan beberapa total, sehingga jika saya memiliki lebih dari ~ 100 ribu baris, itu melaporkan total untuk mereka, kemudian pindah. Ini sebuah contoh:

<100rb baris (output yang diinginkan)

$ 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 garis (harus pipa ke grep "total")

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

Bagaimana cara mendapatkan total sebenarnya wc -l, bukan serangkaian subtotal?

Ehryk
sumber
Menurut stackoverflow.com/questions/2501402/... masalahnya ada pada xargs, bukan wc. Saya masih tertarik dengan cara memperbaikinya, dan saya tidak melihat solusi yang baik dalam jawabannya.
Ehryk
3
Apakah versi Anda wcmendukung --files0-fromopsi? Maka Anda bisa melakukannya{ git ls-files -z ${1} | wc -l --files0-from=- ; }
Mark Plotnick
@ MarkPlotnick Saya pikir itu layak menjadi jawaban.
terdon
Nggak. wc: unrecognized option '--files0-from=-'
Ehryk

Jawaban:

12

Coba ini, dan minta maaf karena sudah jelas:

cat *.cs | wc -l

atau, dengan git:

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

Jika Anda benar-benar ingin output terlihat seperti wcoutput, dengan jumlah individual dan jumlah, Anda dapat menggunakan awkuntuk menambahkan setiap baris:

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

Itu tidak akan berbaris sebaik wcitu, jika itu penting bagi Anda. Untuk melakukan itu, Anda harus membaca seluruh input dan menyimpannya, menghitung total, dan kemudian menggunakan total untuk menghitung lebar bidang sebelum menggunakan lebar bidang itu untuk mencetak output terformat dari garis yang diingat. Seperti proyek renovasi rumah, awkskrip tidak pernah benar-benar selesai.

(Catatan untuk editor yang antusias: ekspresi reguler di bagian pertama awk kondisi adalah dalam kasus ada file yang namanya dimulai dengan "total" dan spasi; jika tidak, kondisinya bisa menjadi jauh lebih sederhana $2 == "total".)

rici
sumber
Itu berfungsi, tetapi hanya menghasilkan total ( git ls-files -z ${1} | xargs -0 cat | wc -l). Namun, saya kehilangan jumlah baris per file yang disediakan oleh wc -l seperti pada contoh pertama saya di atas. Adakah cara untuk mendapatkan yang terbaik dari kedua dunia di sini?
Ehryk
Atau, jika itu terlalu sulit, bagaimana dengan saklar sehingga jika itu akan memecahnya: hanya memberikan total, jika tidak, berikan wc normal per file dengan total output?
Ehryk
@Ehryk: Anda bisa melakukannya dua kali, sekali cara Anda melakukannya dengan grep -vmenjatuhkan garis total, dan sekali cara saya sarankan untuk mendapatkan total total. Atau Anda dapat mencoba solusi awk dalam jawaban yang diedit,
rici
+1: "Seperti proyek renovasi rumah, skrip awk tidak pernah benar-benar selesai."
Ehryk
Itu bekerja seperti pesona. Hasil akhir saya: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

Jika Anda menjalankan Linux, wckemungkinan Anda berasal dari GNU Coreutils dan memiliki--files0-from opsi untuk membaca file (atau stdin) yang berisi daftar panjang nama-nama file yang diakhiri NUL untuk dihitung. The dokumentasi GNU Coreutils wc mengatakan "Hal ini berguna ketika daftar nama file begitu lama bahwa hal itu mungkin melebihi batasan perintah panjang garis. Dalam kasus tersebut, berjalan wc melalui xargs tidak diinginkan karena membagi daftar menjadi potongan-potongan dan membuat cetak wc total untuk setiap sublist daripada untuk seluruh daftar. "

Jadi coba ini:

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

Sunting: Karena Anda wcberasal dari milenium terakhir dan tidak memiliki opsi itu, berikut adalah solusi yang lebih portabel, dengan asumsi Anda memiliki awkdan tidak memiliki file bernama "total". Ini akan memfilter output dari wc, menghilangkan semua totalbaris dan bukannya menjumlahkannya dan mencetak total keseluruhan di akhir.

Satu hal yang saya tidak tahu adalah apakah gitimplementasi alias akan memiliki masalah dengan$1 dan $2di dalam tanda kutip tunggal, yang perlu diteruskan tidak berubah 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"; }' ;
}
Tandai Plotnick
sumber
Saya tidak menjalankan linux, ada di git bash prompt dari Git untuk Windows msysgit.github.io (msysgit).
Ehryk
BAIK. Jadi, xargsdan wcAnda menjalankan berasal dari Cygwin? Bisakah Anda menempelkan output wc --version?
Mark Plotnick
Itu bukan dari instalasi cygwin lengkap:$ 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
Penuh pada windows yang dapat dieksekusi,C:\Program Files (x86)\Git\bin\wc.exe
Ehryk
@Ehryk Msysgit adalah port alat Linux, tetapi cenderung memiliki versi lama, jadi mungkin tidak --files0-from.
Gilles 'SO- stop being evil'
4

Masalahnya adalah xargsmembagi perintah menjadi beberapa kali, sehingga wcmelaporkan total untuk setiap waktu. Anda memiliki beberapa opsi, Anda bisa menjaga semuanya tetap seperti semula dan menguraikan wchasilnya:

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

Anda dapat menyimpan file-file ini:

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

Atau Anda dapat melewati xargssemuanya (diadaptasi dari sini ):

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

Itu akan rusak jika daftar file Anda lebih panjang dari ARG_MAX .

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