Miałem wrażenie, że maksymalna długość pojedynczego argumentu nie była tutaj problemem, tyle że całkowity rozmiar ogólnej tablicy argumentów plus rozmiar środowiska, który jest ograniczony ARG_MAX
. Pomyślałem więc, że coś takiego się powiedzie:
env_size=$(cat /proc/$$/environ | wc -c)
(( arg_size = $(getconf ARG_MAX) - $env_size - 100 ))
/bin/echo $(tr -dc [:alnum:] </dev/urandom | head -c $arg_size) >/dev/null
Ponieważ - 100
jest to więcej niż wystarczające, aby uwzględnić różnicę między rozmiarem środowiska w powłoce a echo
procesem. Zamiast tego dostałem błąd:
bash: /bin/echo: Argument list too long
Po dłuższej zabawie odkryłem, że maksimum to pełny rząd heksów wielkości mniejszy:
/bin/echo \
$(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)/16-1))) \
>/dev/null
Gdy jeden minus zostanie usunięty, błąd powraca. Pozornie maksimum dla pojedynczego argumentu jest w rzeczywistości, ARG_MAX/16
a -1
konta dla bajtu zerowego są umieszczane na końcu ciągu w tablicy argumentów.
Inną kwestią jest to, że gdy argument się powtarza, całkowity rozmiar tablicy argumentów może być bliższy ARG_MAX
, ale wciąż nie do końca:
args=( $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)/16-1))) )
for x in {1..14}; do
args+=( ${args[0]} )
done
/bin/echo "${args[@]}" "${args[0]:6534}" >/dev/null
Użycie "${args[0]:6533}"
tutaj powoduje wydłużenie ostatniego argumentu o 1 bajt i powoduje Argument list too long
błąd. Ta różnica prawdopodobnie nie zostanie uwzględniona w zależności od wielkości środowiska:
$ cat /proc/$$/environ | wc -c
1045
Pytania:
- Czy to jest poprawne zachowanie, czy może gdzieś jest błąd?
- Jeśli nie, czy takie zachowanie jest gdziekolwiek udokumentowane? Czy istnieje inny parametr, który określa maksimum dla pojedynczego argumentu?
- Czy to zachowanie jest ograniczone do Linuksa (czy nawet niektórych jego wersji)?
- Co odpowiada dodatkowej rozbieżności ~ 5 KB między faktycznym maksymalnym rozmiarem tablicy argumentów oraz przybliżonym rozmiarem środowiska i
ARG_MAX
?
Dodatkowe informacje:
uname -a
Linux graeme-rock 3.13-1-amd64 #1 SMP Debian 3.13.5-1 (2014-03-04) x86_64 GNU/Linux
getconf ARG_MAX
zależy od prąduulimit -s
. Ustaw na nieograniczony i uzyskaj niesamowite 4611686018427387903 dla ARG_MAX.Odpowiedzi:
Odpowiedzi
Parametrem określającym maksymalny rozmiar jednego argumentu jest
MAX_ARG_STRLEN
. Brak dokumentacji dla tego parametru oprócz komentarzy wbinfmts.h
:Jak pokazano, Linux ma również (bardzo duży) limit liczby argumentów polecenia.
Limit wielkości pojedynczego argumentu (który różni się od ogólnego limitu argumentów plus środowiska) wydaje się być specyficzny dla Linuksa. Ten artykuł zawiera szczegółowe porównanie
ARG_MAX
i odpowiedniki systemów uniksowych.MAX_ARG_STRLEN
jest omawiany w systemie Linux, ale nie ma wzmianki o żadnym równoważniku w innych systemach.Powyższy artykuł stwierdza również, że
MAX_ARG_STRLEN
został wprowadzony w Linuksie 2.6.23, wraz z szeregiem innych zmian związanych z maksymalnymi argumentami poleceń (omówionymi poniżej). Log / diff dla zatwierdzenia można znaleźć tutaj .Nadal nie jest jasne, co tłumaczy dodatkową rozbieżność między wynikiem
getconf ARG_MAX
a rzeczywistą maksymalną możliwą wielkością argumentów plus środowisko. Powiązana odpowiedź Stephane'a Chazelasa sugeruje, że część przestrzeni jest uwzględniana przez wskaźniki do każdego z ciągów argumentów / środowiska. Jednak moje własne badanie sugeruje, że wskaźniki te nie są tworzone na początkuexecve
wywołania systemowego, gdy może on nadal zwracaćE2BIG
błąd do procesu wywoływania (chociaż wskaźniki do każdegoargv
ciągu są z pewnością tworzone później).Ponadto ciągi są ciągłe w pamięci, o ile widzę, więc żadne luki w pamięci nie powodują tutaj wyrównania. Chociaż jest bardzo prawdopodobne, że będzie czynnikiem w tym, co zużywa dodatkową pamięć. Zrozumienie, co wykorzystuje dodatkową przestrzeń, wymaga bardziej szczegółowej wiedzy o tym, w jaki sposób jądro alokuje pamięć (co jest przydatną wiedzą, więc zbadam ją i zaktualizuję później).
Zamieszanie ARG_MAX
Od Linuksa 2.6.23 (w wyniku tego zatwierdzenia ) wprowadzono zmiany w sposobie obsługi maksymalnych argumentów poleceń, co odróżnia Linuksa od innych systemów uniksopodobnych. Oprócz dodawania
MAX_ARG_STRLEN
iMAX_ARG_STRINGS
, wynikgetconf ARG_MAX
teraz zależy od wielkości stosu i może być inny niżARG_MAX
wlimits.h
.Zwykle wynikiem
getconf ARG_MAX
będzie1/4
rozmiar stosu. Rozważmy następujące wbash
użyciuulimit
, aby uzyskać rozmiar stosu:Jednak powyższe zachowanie zostało nieznacznie zmienione przez to zatwierdzenie (dodane w Linuksie 2.6.25-rc4 ~ 121).
ARG_MAX
inlimits.h
służy teraz jako twarda dolna granica wynikugetconf ARG_MAX
. Jeśli rozmiar stosu jest ustawiony tak, że1/4
rozmiar stosu jest mniejszy niżARG_MAX
wlimits.h
, wówczaslimits.h
zostanie użyta wartość:Zauważ też, że jeśli rozmiar stosu ustawiony jest poniżej minimum możliwego
ARG_MAX
, wówczas rozmiar stosu (RLIMIT_STACK
) staje się górną granicą wielkości argumentu / środowiska przedE2BIG
zwróceniem (chociażgetconf ARG_MAX
nadal będzie wyświetlać wartość wlimits.h
).Ostatnią rzeczą do zapamiętania jest to, że jeśli jądro jest zbudowane bez
CONFIG_MMU
(obsługa sprzętu do zarządzania pamięcią), to sprawdzanieARG_MAX
jest wyłączone, więc limit nie ma zastosowania. ChociażMAX_ARG_STRLEN
iMAX_ARG_STRINGS
nadal mają zastosowanie.Dalsza lektura
ARG_MAX
(i równoważne) wartości w innych systemach uniksopodobnych - http://www.in-ulm.de/~mascheck/various/argmax/MAX_ARG_STRLEN
spowodowało błąd w Automake, który osadzał skrypty powłoki w plikach Makefiles przy użyciush -c
- http://www.mail-archive.com/[email protected]/msg05522.htmlźródło
W
eglibc-2.18/NEWS
W
eglibc-2.18/debian/patches/kfreebsd/local-sysdeps.diff
W
linux/include/uapi/linux/limits.h
I
131072
jest twój$(getconf ARG_MAX)/16-1
, być może powinieneś zacząć od 0.Masz do czynienia z Glibc i Linuksem. Dobrze byłoby załatać getconf również po to, aby otrzymać „właściwą”
ARG_MAX
wartość.Edytować:
Aby trochę wyjaśnić (po krótkiej, ale gorącej dyskusji)
ARG_MAX
Stałą, która jest określona wlimits.h
daje maksymalną długość jednego argumentu podjętej Exec.getconf ARG_MAX
Polecenie zwraca maksymalną wartość skumulowanej wielkości argumenty i środowiska wielkości przekazanego Exec.źródło
eglibc-2.18/NEWS
fragment? Dobrze byłoby przypiąć to do konkretnej wersji jądra.getconf ARG_MAX
o skumulowany rozmiar arg + env (zmienna w najnowszym Linuksie, zobaczulimit -s
i inne pytanie, które podłączyłem), nie dotyczy maksymalnej długości pojedynczego argumentu, dla którego nie ma zapytania sysconf / getconf.Więc @StephaneChazelas słusznie poprawia mnie w komentarzach poniżej - sama powłoka nie określa w żaden sposób maksymalnego rozmiaru argumentu dozwolonego przez twój system, ale raczej jest ustawiona przez twoje jądro.
Jak już kilka innych powiedziało, wydaje się, że jądro ogranicza do 128 kb maksymalnego rozmiaru argumentu, który można przekazać nowemu procesowi z dowolnego innego miejsca przy pierwszym uruchomieniu. Ten problem występuje szczególnie z powodu wielu zagnieżdżonych
$(command substitution)
podpowłok, które muszą być wykonywane w miejscu i przekazywać całość swoich wyników od jednego do drugiego.I ten rodzaj dzikiego zgadywania, ale ponieważ rozbieżność ~ 5kb wydaje się tak zbliżona do standardowego rozmiaru strony systemowej, podejrzewam, że jest poświęcony
bash
zastosowaniom strony do obsługi podpowłoki$(command substitution)
wymaganej do ostatecznego dostarczenia jej wyników i / lub stos funkcji, który wykorzystuje do kojarzeniaarray table
twoich danych. Mogę tylko założyć, że żadne nie jest darmowe.Poniżej pokazuję, że chociaż może to być trochę trudne, możliwe jest przekazywanie bardzo dużych wartości zmiennych powłoki do nowych procesów podczas wywoływania, o ile można to zrobić strumieniowo.
W tym celu użyłem przede wszystkim rur. Ale oceniłem również tablicę powłok w
here-document
wskazanymcat's stdin.
poniżej Wyniki.Ale ostatnia uwaga - jeśli nie potrzebujesz szczególnego kodu przenośnego, uderza mnie to, co
mapfile
może nieco uprościć twoje zadania powłoki.Być może możesz to podwoić, a następnie zrobić to ponownie, jeśli zrobisz to w strumieniach - nie jestem wystarczająco chorobliwy, aby się dowiedzieć - ale na pewno działa, jeśli go przesyłasz.
Próbowałem zmienić
printf
część generatora w drugim wierszu na:Działa również:
Więc może jestem trochę chorobliwy. Używam
zero padding here
i dodaje poprzednią"$arg"
wartość do bieżącej"$arg"
wartości. Dostaję znacznie więcej niż 6500 ...A jeśli zmienię
cat
linię, aby wyglądała tak:Mogę uzyskać liczbę bajtów z
wc.
Pamiętaj, że są to rozmiary każdego klucza wargs
tablicy. Całkowity rozmiar tablicy jest sumą wszystkich tych wartości.źródło
echo $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)*10))) >/dev/null
będą działać poprawnie. Problem występuje tylko wtedy, gdy używasz zewnętrznego polecenia.bash
jakoś to kompresuje?printf
jest wbudowany, więc nie jest wykonywany , a AFAICTcat
nie podaje żadnego argumentu.