„Zbyt długa lista argumentów”: Jak sobie z tym poradzić bez zmiany polecenia?

18

Po uruchomieniu polecenia typu pojawia ls */*/*/*/*.jpgsię błąd

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

Wiem, dlaczego tak się dzieje: dzieje się tak dlatego, że istnieje limit ilości miejsca na argumenty polecenia. Standardowa rada to zmiana polecenia, którego używam, aby uniknąć konieczności zajmowania tak dużej ilości argumentów (np. Użyj findi xargs).

Co jeśli nie chcę zmieniać polecenia? Co jeśli chcę nadal używać tego samego polecenia? Jak mogę sprawić, by rzeczy „po prostu działały” bez wyświetlania tego błędu? Jakie rozwiązania są dostępne?

DW
źródło
Przydatna lektura: Bash FAQ 95 . Bez zmiany polecenia niewiele można zrobić oprócz ponownej kompilacji, aby zwiększyć maksymalny rozmiar listy argumentów lub zmienić strukturę katalogów, tak aby było mniej plików.
jw013,
1
@ jw013 w oparciu o wersję jądra Linuksa można zwiększyć listę argumentów - szczegółowe informacje na temat zmiany w najnowszych systemach można znaleźć na stronie unix.stackexchange.com/a/45161/8979 .
Ulrich Dangel
@UlrichDangel, Tak, jest to możliwe! Zobacz moją odpowiedź; moja odpowiedź pokazuje, jak to zrobić (w Linuksie, z wystarczająco nowym jądrem).
DW,

Odpowiedzi:

26

W systemie Linux maksymalna ilość miejsca na argumenty poleceń wynosi 1/4 ilości dostępnego miejsca na stosie. Tak więc rozwiązaniem jest zwiększenie ilości miejsca dostępnego dla stosu.

Krótka wersja: uruchom coś w stylu

ulimit -s 65536

Dłuższa wersja: domyślna ilość miejsca dostępna dla stosu to około 8192 KB. Ilość dostępnego miejsca możesz zobaczyć w następujący sposób:

$ ulimit -s
8192

Wybierz większą liczbę i ustaw ilość dostępnego miejsca dla stosu. Na przykład, jeśli chcesz spróbować zezwalać na stos do 65536 KB, uruchom to:

$ ulimit -s 65536

Być może będziesz musiał się zastanowić, jak duży musi to być, używając metody prób i błędów. W wielu przypadkach jest to szybkie i-brudny rozwiązanie, które wyeliminuje konieczność modyfikowania polecenia i wypracować składni find, xargsitp (choć zdaję sobie sprawę, że istnieją inne korzyści tak robi).

Uważam, że jest to specyficzne dla systemu Linux. Podejrzewam, że prawdopodobnie nie pomoże na żadnym innym systemie operacyjnym Unix (nie testowanym).

DW
źródło
1
Możesz sprawdzić, jak to działa: $ getconf ARG_MAX 2097152 $ ulimit -s 65535 $ getconf ARG_MAX 16776960
Alex
2

Artykuł w Linux Journal zawiera 4 rozwiązania. Tylko czwarte rozwiązanie nie wymaga zmiany polecenia:

Metoda nr 4 polega na ręcznym zwiększeniu liczby stron przydzielonych w jądrze dla argumentów wiersza poleceń. Jeśli spojrzysz na plik include / linux / binfmts.h, u góry znajdziesz następujące:

/*
 * MAX_ARG_PAGES defines the number of pages allocated for   arguments
 * and envelope for the new program. 32 should suffice, this gives
 * a maximum env+arg of 128kB w/4KB pages!
 */
#define MAX_ARG_PAGES 32

Aby zwiększyć ilość pamięci przeznaczonej na argumenty wiersza poleceń, wystarczy podać wartość MAX_ARG_PAGES o większej liczbie. Po zapisaniu tej edycji po prostu ponownie skompiluj, zainstaluj i uruchom ponownie w nowym jądrze, tak jak normalnie.

Na własnym systemie testowym udało mi się rozwiązać wszystkie moje problemy, podnosząc tę ​​wartość do 64. Po szeroko zakrojonych testach od czasu zmiany nie spotkałem żadnego problemu. Jest to całkowicie oczekiwane, ponieważ nawet przy MAX_ARG_PAGESustawieniu na 64, najdłuższa możliwa linia poleceń, jaką mogłabym stworzyć, zajmowałaby tylko 256 KB pamięci systemowej - niewiele w dzisiejszych standardach sprzętowych systemu.

Zalety metody nr 4 są oczywiste. Teraz możesz po prostu uruchomić polecenie w normalny sposób i zakończy się ono pomyślnie. Wady są równie wyraźne. Jeśli zwiększysz ilość pamięci dostępnej w wierszu poleceń ponad ilość dostępnej pamięci systemowej, możesz utworzyć atak DOS na własny system i spowodować awarię. W szczególności w systemach z wieloma użytkownikami nawet niewielki wzrost może mieć znaczący wpływ, ponieważ każdemu użytkownikowi przydzielana jest dodatkowa pamięć. Dlatego zawsze przeprowadzaj obszerne testy we własnym środowisku, ponieważ jest to najbezpieczniejszy sposób ustalenia, czy metoda nr 4 jest dla Ciebie opłacalna.

Zgadzam się, że ograniczenie jest bardzo denerwujące.

Franck Dernoncourt
źródło
1

Zamiast tego ls */*/*/*/*.jpgspróbuj:

echo */*/*/*/*.jpg | xargs ls

xargs(1) wie, jaka jest maksymalna liczba argumentów w systemie, i rozbije standardowe wejście, aby wywołać określony wiersz poleceń wiele razy, nie więcej niż argumenty powyżej tego limitu, cokolwiek to jest (możesz również ustawić go na wartość niższą niż maksimum systemu operacyjnego za pomocą -nopcji).

Załóżmy na przykład, że limit wynosi 3 argumenty i masz pięć plików. W takim przypadku xargswykona się lsdwukrotnie:

  1. ls 1.jpg 2.jpg 3.jpg
  2. ls 4.jpg 5.jpg

Często jest to całkowicie odpowiednie, ale nie zawsze - na przykład nie możesz polegać na ls(1) sortowaniu wszystkich wpisów dla siebie poprawnie, ponieważ każde osobne lswywołanie posortuje tylko podzbiór wpisów mu podanych xargs.

Chociaż możesz podnieść limit, jak sugerują inni, nadal będzie limit - i pewnego dnia twoja kolekcja JPG ponownie go przerośnie. Powinieneś przygotować swój skrypt (y) do obsługi nieskończonej liczby ...

Michaił T.
źródło
Dzięki za pomysł! To nie jest złe obejście. Dwa zastrzeżenia: 1. To łamie katalogi i nazwy plików, które mają spacje w nazwie, więc nie jest to idealny zamiennik. 2. Czy będzie to dotyczyło tego samego problemu z powłokami Argument list too long, ale echozamiast nich lsw powłokach, w których echonie ma wbudowanego polecenia powłoki? (Może to nie jest problem w większości pocisków, więc może to nie ma znaczenia.)
DW
1
Tak, znaki specjalne w nazwach plików stanowią problem. Najlepszym rozwiązaniem jest użycie predykatu - i findprzekazanie -print0jego wyników za xargspomocą -0opcji. echojest wbudowaną powłoką i nie podlega ograniczeniom wiersza poleceń exec(3).
Michaił T.