Почему утилита wc генерирует несколько строк с "total"?
Я использую утилиту wc в сценарии оболочки, который я запускаю из Cygwin, и я заметил, что в выводе есть более одной строки с "total".
Следующая функция используется для подсчета количества строк в моих исходных файлах:
count_curdir_src() {
find . '(' -name '*.vb' -o -name '*.cs' ')' \
-a '!' -iname '*.Designer.*' -a '!' -iname '.svn' -print0 | \
xargs -0 wc -l
}
Но его вывод для определенного каталога выглядит так:
$ find . '(' -name '*.vb' -o -name '*.cs' ')' -a '!' -iname '*.Designer.*' -a '!' -iname '.svn' -print0 | xargs -0 wc -l
19 ./dirA/fileABC.cs
640 ./dirA/subdir1/fileDEF.cs
507 ./dirA/subdir1/fileGHI.cs
2596 ./dirA/subdir1/fileJKL.cs
(...many others...)
58 ./dirB/fileMNO.cs
36 ./dirB/subdir1/filePQR.cs
122200 total
6022 ./dirB/subdir2/subsubdir/fileSTU.cs
24 ./dirC/fileVWX.cs
(...)
36 ./dirZ/Properties/AssemblyInfo.cs
88 ./dirZ/fileYZ.cs
25236 total
Похоже, что wc сбрасывается где-то в процессе. Это не может быть вызвано пробелами в именах файлов или каталогов, потому что я использую -print0
вариант. И это происходит только тогда, когда я запускаю его на моем самом большом исходном дереве.
Итак, это ошибка в wc или в Cygwin? Или что-то другое? Справочная страница по wc гласит:
Выведите количество строк, слов и байтов для каждого ФАЙЛА, а также итоговую строку, если указано более одного ФАЙЛА.
В нем ничего не говорится о нескольких общих строках (промежуточные итоговые значения или что-то в этом роде), так кто здесь виноват?
4 ответа
Вы вызываете wc несколько раз - по одному разу для каждого "пакета" входных аргументов, предоставляемых xargs. Вы получаете одну сумму за партию.
Одна из альтернатив заключается в использовании временного файла и --files0-from
вариант для wc
:
$ find . '(' -name '*.vb' -o -name '*.cs' ')' -a '!' -iname '*.Designer.*' -a
'!' -iname '.svn' -print0 > files
$ wc --files0-from files
Что происходит то xargs
бежит wc
многократно. xargs
по умолчанию в каждом вызове команды, которую он должен выполнить, упаковывается столько аргументов, сколько он может, но если файлов слишком много, команда будет запускаться несколько раз для подмножеств файлов.
Я вижу несколько способов исправить это. Первое, что сломается, если у вас слишком много файлов, это пропустить xargs
и использовать оболочку. Это может не сработать на Cygwin, но будет выглядеть так:
wc -l $(find . '(' -name '*.vb' -o -name '*.cs' ')' \
-a '!' -iname '*.Designer.*' -a '!' -iname '.svn' )
и вы также потеряете возможности print0.
Другой должен использовать awk
(или же perl
) скрипт для обработки вывода вашего find
/xargs
комбо, пропустите "итоговые" строки и подведите итоги самостоятельно.
Длина командной строки намного более ограничена в Cygwin, чем в стандартном Linux-боксе, и xargs
должен разделить входные данные, чтобы соблюдать эти ограничения. Вы можете проверить пределы с xargs --show-limits
:
На Cygwin:
$ xargs --show-limits < /dev/null
Your environment variables take up 4913 bytes
POSIX upper limit on argument length (this system): 25039
POSIX smallest allowable upper limit on argument length (all systems): 4096
Maximum length of command we could actually use: 20126
Size of command buffer we are actually using: 25039
По сентосам:
$ xargs --show-limits < /dev/null
Your environment variables take up 1816 bytes
POSIX upper limit on argument length (this system): 2617576
POSIX smallest allowable upper limit on argument length (all systems): 4096
Maximum length of command we could actually use: 2615760
Size of command buffer we are actually using: 131072
И, основываясь на ответе @JonSkeet, вам не нужно создавать дополнительный файл, вы можете направить результаты поиска напрямую в wc, передав -
в качестве аргумента --files0-from
:
find . -name '*.vb' -print0 | wc -l --files0-from=-
Чтобы избежать генерации нескольких строк с "общим" количеством при подаче wc
Утилита с огромным количеством путей к файлам в качестве аргументов командной строки, вы можете использовать промежуточный xargs
в cat
содержимое файлов на стандартный wc
(см. вывод команды find в xargs, wc дает необоснованные итоги).
Это обходной путь, если ваш wc
команда не имеет --files0-from
как упомянуто Ксавье.
count_curdir_src() (
export LC_ALL=C
find . -name '*.vb' -print0 | xargs -0 -n 1000 cat | wc -l
)