2015-04-22 15 views
7

Sto eseguendo Python 3.4.3 su Linux 3.16.0. Voglio usare subprocess.Popen per eseguire un comando con un singolo argomento lungo (una complessa invocazione di Bash), circa 200 KiB.Perché il sottoprocesso.Popen ha un limite di lunghezza dell'argomento inferiore a quello segnalato dal sistema operativo?

Secondo getconf e xargs, questo dovrebbe essere ben entro i miei limiti:

$ 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 

Tuttavia, Python viene a mancare con limiti molto più piccoli:

>>> 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 

Nota che il limite di Python è più o meno lo stesso come il "reale utilizzo" del buffer di comando xargs segnalazioni. Questo suggerisce che xargs è abbastanza intelligente per iniziare con un limite più piccolo e aumentarlo secondo necessità, ma Python non lo è.

Domande:

  1. Perché limite inferiore al limite del sistema operativo 2MiB Python?
  2. Posso aumentare il limite Python?
  3. Se sì, come?
+2

Non utilizzare l'API "single string" di 'subprocess.Popen()'. Passa sempre una lista invece. Evita 'shell = True'. –

+0

In questo caso, in realtà sto facendo affidamento sulle funzionalità di Bash nel sottoprocesso. – Reid

+0

Provare il sottoprocesso.Popen ('echo $ SHELL') '. Penso che ti darà '/ bin/sh', non bash. –

risposta

9

La dimensione massima per un singolo argomento stringa è limitato a 131072. Non ha nulla a che fare con python:

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

In realtà è il MAX_ARG_STRLEN che decide la dimensione massima per una singola stringa:

E come limite aggiuntivo dal 2.6.23, un argomento non deve essere più lungo di MAX_ARG_STRLEN (131072). Questo potrebbe diventare rilevante se si genera una chiamata lunga come "sh -c" generata con argomenti lunghi "". (sottolineato da Xan Lopez e Ralf Wildenhues)

Vedi this discussion of ARG_MAX, alla voce "Numero di argomenti e la lunghezza massima di un argomento", e this question su unix.stackexchange.

Si può vedere in 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 

È possibile passare più stringhe di lunghezza 131071:

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

Ma una singola stringa arg Non si può essere più lungo di 131071 byte.

+0

Grazie. Una soluzione è passare i comandi di Bash usando 'stdin' del sottoprocesso. Durante i miei test, sono stato in grado di eseguire singoli argomenti di 16MiB, il che è fonte di confusione perché dovrebbe violare tutti i limiti. Tuttavia, non ho seguito ulteriormente perché la soluzione basata su Bash stava diventando troppo pelosa e sto tornando al puro Python. – Reid

+0

Ci sono diversi modi per aggirarlo usando bash, loop, xargs ecc. A seconda di cosa stai facendo. –

-3

This altra domanda è simile alla tua, ma per Windows. Come in quello scenario, puoi ignorare qualsiasi limite della shell evitando l'opzione shell=True.

Altrimenti, è possibile fornire un elenco di file a subprocess.Popen() come fatto in tale scenario e come suggerito da @Aaron Digulla.

+2

Apparentemente diverso da Windows, il limite non proviene dalla shell su UNIX. – Reid

Problemi correlati