Как получить общее количество строк с помощью `wc -l`?

12

Я добавил псевдоним git для подсчета количества строк определенных файлов в моей истории:

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

Тем не менее, wc -lотчет о нескольких итогах, так что, если у меня есть более чем 100 000 строк, он сообщает для них итоги, а затем идет дальше. Вот пример:

<100 тыс. Строк (желаемый вывод)

$ 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 линии (пришлось трубы grep "total")

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

Как получить истинную сумму wc -l, а не ряд промежуточных итогов?

Ehryk
источник
Согласно stackoverflow.com/questions/2501402/… проблема с xargs, а не wc. Мне все еще интересно, как это исправить, и я не вижу хорошего решения в ответах.
Эрик
3
Ваша версия wcподдерживает --files0-fromопцию? Тогда вы можете сделать{ git ls-files -z ${1} | wc -l --files0-from=- ; }
Марк Плотник
@MarkPlotnick Я думаю, это заслуживает ответа.
Terdon
Нет. wc: unrecognized option '--files0-from=-'
Эрик

Ответы:

12

Попробуйте это, и извинения за очевидность:

cat *.cs | wc -l

или с помощью git:

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

Если вы действительно хотите, чтобы выходные данные выглядели как wcвыходные, с индивидуальными подсчетами и суммой, вы можете использовать awkдля суммирования отдельных строк:

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

Это не будет выстроено так хорошо, как wcв случае, если это важно для вас. Чтобы сделать это, вам нужно прочитать весь ввод и сохранить его, вычисляя итоговое значение, а затем использовать итоговое значение для вычисления ширины поля, прежде чем использовать эту ширину поля для печати форматированного вывода запомненных строк. Как и проекты по реконструкции дома, awkсценарии никогда не заканчиваются.

(Примечание для редакторов-энтузиастов: регулярное выражение в первом awkусловии есть в случае, если существует файл, имя которого начинается с «total» и пробела; в противном случае условие могло бы быть намного проще $2 == "total".)

RICi
источник
Это работает, но выводит всего только ( git ls-files -z ${1} | xargs -0 cat | wc -l). Тем не менее, мне не хватает количества строк в файле, которое предоставляет wc -l, как в моем первом примере выше. Любой способ получить лучшее из обоих миров здесь?
Эрик
Или, если это слишком сложно, как насчет переключателя, который, если он разбил бы его: просто дайте общее значение, если это не так, дайте обычный wc на файл с общим выводом?
Эрик
@Ehryk: вы могли бы просто сделать это дважды, один раз так, как вы это делали, grep -vчтобы отбросить общее количество строк, и один раз, как я предлагаю получить общее количество. Или вы можете попробовать решение awk в отредактированном ответе
rici
+1: «Как и проекты по реконструкции дома, сценарии awk никогда не заканчиваются».
Эрик
Это работает как шарм. Мой окончательный результат: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

Если вы работаете в Linux, вы, wcвероятно, исходите из GNU Coreutils и --files0-fromможете прочитать файл (или stdin), содержащий произвольно длинный список имен файлов, заканчивающихся NUL, для подсчета. В документации GNU Coreutils wc сказано: «Это полезно, когда список имен файлов настолько велик, что может превышать ограничение длины командной строки. В таких случаях запуск wc через xargs нежелателен, поскольку он разбивает список на части и делает вывод wc итого по каждому подсписку, а не по всему списку. "

Так что попробуйте это:

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

Изменить: так как ваш wcс прошлого тысячелетия и не имеет этой опции, вот более портативное решение, если у вас есть awkи нет файлов с именем «всего». Он отфильтрует выходные данные wc, пропустив любые totalстроки, суммируя их и распечатав итоговую сумму в конце.

Одна вещь, которую я не знаю, это то, gitбудут ли у реализации псевдонима проблемы с одиночными кавычками $1и $2внутри, которые нужно передать без изменений 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"; }' ;
}
Марк Плотник
источник
Я не работаю под Linux, это в приглашении git bash Git для Windows msysgit.github.io (msysgit).
Эрик
OK. Значит xargsи wcты бежишь от Cygwin? Можете ли вы вставить вывод wc --version?
Марк Плотник
Они не из полной установки $ 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.
Cygwin
Это полный исполняемый файл Windows,C:\Program Files (x86)\Git\bin\wc.exe
Эрик
@Ehryk Msysgit - это порт инструментов Linux, но он, как правило, имеет старые версии, поэтому может и не иметь --files0-from.
Жиль "ТАК - перестань быть злым"
4

Проблема в том, xargsчто команда разбивается на несколько запусков, поэтому wcкаждый раз выдает отчет об итогах. У вас есть несколько вариантов, вы можете оставить все как есть и проанализировать wcвывод:

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

Вы можете просмотреть файлы:

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

Или вы можете xargsвообще пропустить (адаптировано отсюда ):

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

Это сломается, если ваш список файлов длиннее, чем ARG_MAX .

Тердон
источник
-1
j=0; for i in *.php *.js *.css; do let j+=`wc -l $i | awk {'print $1'}`; done; echo $j;
NilsonCain
источник