2010-03-23 8 views
5

Używam narzędzia wc w skrypcie powłoki, który uruchamiam z Cygwin, i zauważyłem, że istnieje więcej niż jedna linia z "całkowitą" w jej wyniku.Dlaczego narzędzie wc generuje wiele linii z "całkowitą"?

Poniższa funkcja służy do policzyć liczbę wierszy w moich plikach źródłowych:

count_curdir_src() { 
    find . '(' -name '*.vb' -o -name '*.cs' ')' \ 
     -a '!' -iname '*.Designer.*' -a '!' -iname '.svn' -print0 | \ 
    xargs -0 wc -l 
} 

Ale jego wyjście do pewnego katalogu wygląda następująco:

$ 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 

Wygląda wc resetuje gdzieś w tym procesie. Nie może być spowodowane znakami spacji w nazwach plików lub nazwach katalogów, ponieważ używam opcji -print0. I dzieje się tak tylko wtedy, gdy uruchomię go na moim największym drzewie źródeł.

Czy to błąd w WC, czy w Cygwin? Albo coś innego? Wc podręcznika mówi:

Drukuj nowej linii, słowo, a bajt liczy dla każdego pliku, a całkowitą linię jeśli więcej niż jeden plik jest określona.

Nie wspomina nic o wielu liniach łącznych (łączna suma się liczy, czy coś), więc kto jest tutaj winny?

Odpowiedz

2

Wywołujesz wc wiele razy - raz dla każdej "partii" argumentów wejściowych dostarczonych przez xargs. Dostajesz jedną sumę na partię.

Alternatywą jest użycie pliku tymczasowego i opcję --files0-from dla wc:

$ find . '(' -name '*.vb' -o -name '*.cs' ')' -a '!' -iname '*.Designer.*' -a 
    '!' -iname '.svn' -print0 > files 

$ wc --files0-from files 
+1

Dwie rzeczy: powody, dla których podział xargs jest wsadowy, można zobaczyć w 'xargs --show-limits', który pokazuje ograniczenia w twoim systemie. Możesz uniknąć tworzenia pliku za pomocą potoków i używając '--files0-from = -', który czyta ze stdin – Xavier

+0

@Xavier: Może warto dodać własną odpowiedź z tymi informacjami. –

4

Co się dzieje jest to, że xargs pracuje wc wielokrotnie. xargs domyślnie pakuje tyle argumentów ile myśli, że może do każdego wywołania polecenia, które ma uruchomić, ale jeśli jest za dużo plików, uruchomi polecenie wiele razy w podzbiorze plików.

Istnieje kilka sposobów, aby to naprawić. Pierwszym, które się zepsuje, jeśli masz zbyt dużo plików, jest pominięcie xargs i użycie powłoki. To może nie działać dobrze na Cygwin, ale będzie wyglądać następująco:

wc -l $(find . '(' -name '*.vb' -o -name '*.cs' ')' \ 
    -a '!' -iname '*.Designer.*' -a '!' -iname '.svn') 

a także stracić możliwości print0.

Drugim jest użycie awk (lub perl) skrypt do przetwarzania danych wyjściowych swojej find/xargs combo, pomiń „ogółem” wiersze, a Podsumowując łącznie samodzielnie.

2

Długość linii poleceń jest znacznie mniejsza pod cygwin niż w standardowym pudełku linuksowym, a xargs musi podzielić dane wejściowe, aby zachować te ograniczenia.Można sprawdzić limity xargs --show-limits:

na 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 

na CentOS:

$ 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 

i budowania na użytkownika @ JonSkeet odpowiedź, że nie ma potrzeby tworzenia dodatkowego pliku , możesz wyprowadzić wyniki wyszukiwania bezpośrednio do wc, przekazując - jako argument do --files0-from:

find . -name '*.vb' -print0 | wc -l --files0-from=- 
0

Aby uniknąć generowania wielu wierszy z liczbą "całkowitą" podczas podawania narzędzia wc z ogromną liczbą ścieżek plików jako argumentów wiersza poleceń, można użyć pośredniego xargs do cat zawartości plików na stdin z wc (patrz piping output of find to xargs wc gives unreasonable totals).

Jest to obejście, jeśli twoje polecenie wc nie ma wartości --files0-from, o której wspomniał Xavier.

count_curdir_src() (
    export LC_ALL=C 
    find . -name '*.vb' -print0 | xargs -0 -n 1000 cat | wc -l 
)