Як отримати єдину загальну кількість рядків з `wc -l`?


12

Я додав псевдонім git, щоб дати мені кількість рядків конкретних файлів у моїй історії:

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

Однак wc -lповідомляє про декілька підсумків, так що якщо у мене більше ~ 100 К рядків, він повідомляє про них загальну суму, а потім рухається далі. Ось приклад:

<100k рядків (бажаний вихід)

$ 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, а не серію підметалів?


Згідно з 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".)


Це працює, але виводить лише загальний ( 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"}'
Егрик

7

Якщо ви працюєте з Linux, wcможливо, він походить від GNU Coreutils і має --files0-fromможливість прочитати файл (або stdin), що містить довільно довгий список імен файлу, що закінчується NUL. У документації GcU Coreutils wc написано: "Це корисно, коли список назв файлів настільки довгий, що може перевищувати обмеження довжини командного рядка. У таких випадках запуск wc через xargs небажаний, оскільки він розбиває список на шматки та робить друк wc загалом для кожного підспілу, а не для всього списку. "

Тому спробуйте це:

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

Редагувати: Оскільки ви wcє останнім тисячоліттям і не має цього варіанту, тут є більш портативне рішення, якщо припустити, що у вас є awkі немає файлів з назвою "total". Він буде фільтрувати вихід 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).
Егрик

ГАРАЗД. Отже, xargsі wcти працюєш від Cygwin? Чи можете ви вставити вихід wc --version?
Марк Плотнік

Вони не з повного встановлення cygwin:$ 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.
Егрик

Це повноцінна 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 .


Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.