2015-04-22 10 views
7

Mam Python 3.4.3 na Linux 3.16.0. Chcę użyć polecenia subprocess.Popen, aby uruchomić polecenie z długim pojedynczym argumentem (złożonym wywołaniem Bash), około 200 KB.Dlaczego limit długości argumentu podprocesu.Popen jest mniejszy niż to, co raportuje system operacyjny?

Według getconf i xargs, to powinno być dobrze w moich granicach:

$ getconf ARG_MAX 
2097152 
$ xargs --show-limits < /dev/null 
Your environment variables take up 3364 bytes 
POSIX upper limit on argument length (this system): 2091740 
POSIX smallest allowable upper limit on argument length (all systems): 4096 
Maximum length of command we could actually use: 2088376 
Size of command buffer we are actually using: 131072 

Jednak Python nie powiedzie się z dość mniejszych limitów:

>>> subprocess.Popen('echo %s > /dev/null' % ('a' * (131072-4096)), shell=True, executable='/bin/bash') 
<subprocess.Popen object at 0x7f4613b58410> 
>>> subprocess.Popen('echo %s > /dev/null' % ('a' * (262144-4096)), shell=True, executable='/bin/bash') 
Traceback (most recent call last): 
    [...] 
OSError: [Errno 7] Argument list too long 

nocie, że limit Python jest mniej więcej taka sama jako raporty o "faktycznie używanym" buforze poleceń xargs. Sugeruje to, że xargs jest wystarczająco inteligentny, aby zacząć od mniejszego limitu i zwiększyć go w razie potrzeby, ale Python nie jest.

Pytania:

  1. Dlaczego Python ograniczają mniejsza niż limit OS z 2MiB?
  2. Czy mogę zwiększyć limit Pythona?
  3. Jeśli tak, w jaki sposób?
+2

Nie używaj API "pojedynczego ciągu" z 'subprocess.Popen()'. Zawsze należy podać listę. Unikaj 'shell = True'. –

+0

W tym przypadku faktycznie polegam na funkcjach Bash w podprocesie. – Reid

+0

Spróbuj 'podprocesu.Popen ('echo $ SHELL') '. Myślę, że da ci '/ bin/sh', a nie bash. –

Odpowiedz

9

Maksymalny rozmiar pojedynczego argumentu ciąg jest ograniczony do 131072. To nie ma nic wspólnego z Pythona:

~$ /bin/echo "$(printf "%*s" 131071 "a")">/dev/null 
~$ /bin/echo "$(printf "%*s" 131072 "a")">/dev/null 
bash: /bin/echo: Argument list too long 

To jest rzeczywiście MAX_ARG_STRLEN, który decyduje o wielkości max dla pojedynczego sznurka:

Jako dodatkowy limit od 2.6.23 jeden argument nie może być dłuższy niż MAX_ARG_STRLEN (131072). Może to mieć znaczenie, jeśli wygenerujesz długie połączenie, takie jak "sh -c" generowane z długimi argumentami "". (wskazane przez Xan Lopez i Ralf Wildenhues)

Zobacz this discussion of ARG_MAX pod „liczba argumentów i maksymalnej długości jednego argumentu” i this question na unix.stackexchange.

Widać to w binfmts.h:

/* 
* These are the maximum length and maximum number of strings passed to the 
* execve() system call. MAX_ARG_STRLEN is essentially random but serves to 
* prevent the kernel from being unduly impacted by misaddressed pointers. 
* MAX_ARG_STRINGS is chosen to fit in a signed 32-bit integer. 
*/ 
#define MAX_ARG_STRLEN (PAGE_SIZE * 32) 
#define MAX_ARG_STRINGS 0x7FFFFFFF 

~$ echo $(($(getconf PAGE_SIZE)*32)) 
131072 

można przekazać wiele ciągi o długości 131071:

subprocess.check_call(['echo', "a"*131071,"b"*131071], executable='/bin/bash',stdout=open("/dev/null","w")) 

Ale jedno arg ciąg nie może być dłuższy niż 131071 bajtów.

+0

Dziękuję. Jednym z obejść jest przekazanie komend Bash za pomocą 'stdin' podprocesu. Podczas moich testów udało mi się wykonać pojedyncze argumenty 16 MB, co jest mylące, ponieważ powinno to naruszyć wszystkie granice. Nie podjąłem jednak dalszych kroków, ponieważ rozwiązanie bazujące na Bashu stało się zbyt owłosione i powracam do czystego Pythona. – Reid

+0

Istnieje kilka różnych sposobów obejścia go za pomocą basha, pętli, xargs itp .. w zależności od tego, co robisz. –

-3

This inne pytanie jest podobne do twojego, ale dla Windows. Podobnie jak w tym scenariuszu, można ominąć wszelkie ograniczenia powłoki, unikając opcji shell=True.

W przeciwnym razie można dostarczyć listę plików do subprocess.Popen(), jak w tym scenariuszu, i zgodnie z sugestią @Aarona Digulla.

+2

Podobno w przeciwieństwie do systemu Windows limit nie pochodzi z powłoki w systemie UNIX. – Reid