Почему утилита 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 
)
Другие вопросы по тегам