Lista argumentów jest za długa dla ls

48

Podczas próby otwarcia ls *.txt | wc -lkatalogu zawierającego wiele plików pojawia się następujący błąd :

-bash: /bin/ls: Argument list too long

Czy próg tej „listy argumentów” zależy od dystrybucji lub specyfikacji komputera? Zwykle przesyłam wynik tak dużego wyniku do niektórych innych poleceń ( wc -lna przykład), więc nie interesują mnie ograniczenia terminala.

zahypeti
źródło
6
Że liczy się jako analizowania lswyjście „s , co jest złym pomysłem, więc lepiej unikać. Aby zliczyć, zobacz Jaki jest najlepszy sposób na policzenie liczby plików w katalogu? , w przypadku trudnego obejścia sprawdź, dlaczego dla pętli nie występuje błąd „zbyt długi argument”? .
manatwork
@manatwork Tak, widziałem też te pytania. Zastanawiam się, czy jest lepszy sposób na użycie lub przekierowanie długich danych wyjściowych polecenia w bardziej ogólny sposób.
możesz użyć getconf ARG_MAX, aby uzyskać limit w większości systemów uniksowych
Prasanth

Odpowiedzi:

49

Twój komunikat o błędzie lista argumentów zbyt długo pochodzi z * o ls *.txt.

Ograniczenia te stanowią bezpieczeństwo zarówno dla programów binarnych, jak i dla jądra. Na tej stronie zobaczysz więcej informacji o tym, a także o tym, jak jest wykorzystywany i obliczany.

Nie ma takiego ograniczenia rozmiaru rury. Możesz więc po prostu wydać to polecenie:

find -type f -name '*.txt'  | wc -l

Uwaga: we współczesnym systemie Linux dziwne znaki w nazwach plików (np. Nowe wiersze) będą oznaczone znakami ucieczki za pomocą narzędzi takich jak lslub find, ale nadal wyświetlane z * . Jeśli korzystasz ze starego Uniksa, będziesz potrzebować tego polecenia

find -type f -name '*.txt' -exec echo \;  | wc -l

NB2: Zastanawiałem się, jak stworzyć plik z nową linią w nazwie. To nie jest takie trudne, kiedy znasz już sztuczkę:

touch "hello
world"
Coren
źródło
1
Zmodyfikowałem go nieco, aby działał w przypadku, gdy istnieją nazwy plików z nowymi liniami. Możesz także dodać a, -maxdepth 1jeśli nie zamierzasz liczyć plików w podkatalogach.
Shawn J. Goff
Nie potrzebujesz -exec echo \;.
Mikel
@ ShawnJ.Goff Przetestowałem to. W obecnej wersji GNU find nie ma potrzeby używania echa
Coren
@Coren @Mikel - nie wszyscy mają GNU find. W findsystemie OS X i systemach opartych na busyboksie, i jak sądzę, każdy system oparty na BSD pokazuje nazwę pliku z nową linią, co może zepsuć się z liczbą.
Shawn J. Goff
Co? wc -lliczy nowe linie. Więc chce go mieć nowe linie.
Mikel
11

Zależy to głównie od twojej wersji jądra Linux.

Powinieneś być w stanie zobaczyć limit swojego systemu, uruchamiając

getconf ARG_MAX

która określa maksymalną liczbę bajtów, jaką wiersz poleceń może mieć po rozwinięciu przez powłokę.

W Linuksie <2.6.23 limit wynosi zwykle 128 KB.

W Linuksie> = 2.6.25 limit wynosi 128 KB lub 1/4 wielkości stosu (patrz ulimit -s), w zależności od tego, która wartość jest większa.

Zobacz stronę manuala execve (2), aby poznać wszystkie szczegóły.


Niestety, potokowanie ls *.txtnie rozwiąże problemu, ponieważ limit jest w systemie operacyjnym, a nie w powłoce.

Powłoka rozszerza *.txt, a następnie próbuje zadzwonić

exec("ls", "a.txt", "b.txt", ...)

i masz tyle pasujących plików *.txt, że przekraczasz limit 128 KB.

Będziesz musiał zrobić coś takiego

find . -maxdepth 1 -name "*.txt" | wc -l

zamiast.

(I zobacz poniższe komentarze Shawna J. Goffa na temat nazw plików zawierających nowe linie).

Mikel
źródło
Przepraszamy za brak możliwości głosowania za odpowiedzią. Potrzebujesz więcej reputacji. :( Dziękuję wszystkim !!
Czy możesz wyjaśnić, co oznacza .i co -maxdepth 1oznacza w ostatnim wierszu? Dzięki! : D
Guilherme Salomé
2
@ GuilhermeSalomé .oznacza bieżący katalog, co -maxdepth 1oznacza, że ​​nie przegląda on podkatalogów. To miało pasować do tych samych plików, co *.txt.
Mikel
9

Kolejne obejście:

ls | grep -c '\.txt$'

Mimo że lsprodukuje więcej danych wyjściowych niż ls *.txtprodukuje (lub próbuje produkować), nie napotyka problemu „zbyt długich argumentów”, ponieważ nie przekazujesz żadnych argumentów ls. Zauważ, że grepbierze wyrażenie regularne zamiast wzorca pasującego do pliku.

Możesz użyć:

ls -U | grep -c '\.txt$'

(zakładając, że Twoja wersja lsobsługuje tę opcję). Mówi lsto, aby nie sortować danych wyjściowych, co może zaoszczędzić zarówno czas, jak i pamięć - w tym przypadku kolejność nie ma znaczenia, ponieważ tylko zliczasz pliki. Zasoby wydane na sortowanie danych wyjściowych zwykle nie są znaczące, ale w tym przypadku wiemy już, że masz bardzo dużą liczbę *.txtplików.

I powinieneś rozważyć reorganizację swoich plików, aby nie było ich tak wiele w jednym katalogu. To może, ale nie musi być wykonalne.

Keith Thompson
źródło
1

MAX_ARG_PAGES wydaje się być parametrem jądra. Używanie findi xargsjest typową kombinacją do ograniczenia tego limitu, ale nie jestem pewien, czy to zadziała wc.

Pipingowanie wyjścia find . -name \*\.txtdo pliku i zliczanie linii w tym pliku powinno służyć jako obejście.

Bram
źródło
Możesz zrobić wszystko z lswyjściem, to nie rozwiąże. Dopóki znak wieloznaczny * .txt zostanie rozwinięty powyżej limitu, nie powiedzie się nawet przed uruchomieniem lsi wygenerowaniem jakiegokolwiek wyniku.
manatwork
To prawda, zaktualizowałem swoją odpowiedź.
Bram
Lepszy. Aby jednak zastąpić go ls, należy określić, -maxdepth 1aby unikać rekurencyjnego skanowania podkatalogów.
manatwork
Przepraszamy za brak możliwości głosowania za odpowiedzią. Potrzebujesz więcej reputacji. :(
0

To może być brudne, ale działa na moje potrzeby i w ramach moich kompetencji. Nie sądzę, że działa bardzo szybko, ale pozwoliło mi to kontynuować.

ls | grep jpg | <something>

Otrzymywałem listę 90 000 plików JPG i przesyłałem je do avconv, aby wygenerować upływ czasu.

Wcześniej używałem ls * .jpg | avconv, zanim natknąłem się na ten problem.

Richard Bettridge
źródło