Tenía la impresión de que la longitud máxima de un solo argumento no era el problema aquí, sino el tamaño total de la matriz de argumentos general más el tamaño del entorno, que se limita a ARG_MAX
. Por lo tanto, pensé que algo como lo siguiente tendría éxito:
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
Con el - 100
ser más que suficiente para explicar la diferencia entre el tamaño del entorno en el shell y el echo
proceso. En cambio, recibí el error:
bash: /bin/echo: Argument list too long
Después de jugar un rato, descubrí que el máximo era un orden hexadecimal completo de menor magnitud:
/bin/echo \
$(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)/16-1))) \
>/dev/null
Cuando se elimina el menos uno, el error regresa. Aparentemente, el máximo para un solo argumento es en realidad ARG_MAX/16
y las -1
cuentas para el byte nulo colocado al final de la cadena en la matriz de argumentos.
Otro problema es que cuando se repite el argumento, el tamaño total de la matriz de argumentos puede estar más cerca ARG_MAX
, pero aún no del todo:
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
Usar "${args[0]:6533}"
aquí hace que el último argumento 1 byte sea más largo y da el Argument list too long
error. Es improbable que esta diferencia se deba al tamaño del entorno dado:
$ cat /proc/$$/environ | wc -c
1045
Preguntas:
- ¿Es este comportamiento correcto o hay algún error en alguna parte?
- Si no, ¿se documenta este comportamiento en alguna parte? ¿Hay otro parámetro que defina el máximo para un solo argumento?
- ¿Se limita este comportamiento a Linux (o incluso a versiones particulares de este)?
- ¿Qué explica la discrepancia adicional de ~ 5 KB entre el tamaño máximo real de la matriz de argumentos más el tamaño aproximado del entorno y
ARG_MAX
?
Información adicional:
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
depende de la corrienteulimit -s
. Configúrelo como ilimitado y obtenga un increíble 4611686018427387903 para ARG_MAX.Respuestas:
Respuestas
El parámetro que define el tamaño máximo para un argumento es
MAX_ARG_STRLEN
. No hay documentación para este parámetro aparte de los comentarios enbinfmts.h
:Como se muestra, Linux también tiene un límite (muy grande) en el número de argumentos para un comando.
Un límite en el tamaño de un argumento único (que difiere del límite general en argumentos más el entorno) parece ser específico de Linux. Este artículo ofrece una comparación detallada
ARG_MAX
y equivalentes en sistemas similares a Unix.MAX_ARG_STRLEN
se discute para Linux, pero no se menciona ningún equivalente en ningún otro sistema.El artículo anterior también establece que
MAX_ARG_STRLEN
se introdujo en Linux 2.6.23, junto con una serie de otros cambios relacionados con los máximos de argumentos de comando (que se analizan a continuación). El log / diff para el commit se puede encontrar aquí .Todavía no está claro qué explica la discrepancia adicional entre el resultado
getconf ARG_MAX
y el tamaño real máximo posible de argumentos más el entorno. La respuesta relacionada de Stephane Chazelas sugiere que parte del espacio se explica por punteros a cada una de las cadenas de argumento / entorno. Sin embargo, mi propia investigación sugiere que estos punteros no se crean temprano en laexecve
llamada del sistema cuando aún puede devolver unE2BIG
error al proceso de llamada (aunque los punteros a cadaargv
cadena ciertamente se crean más adelante).Además, las cadenas son contiguas en la memoria hasta donde puedo ver, por lo que no hay huecos de memoria debido a la alineación aquí. Aunque es muy probable que sea un factor dentro de lo que sea que use la memoria extra. Comprender qué utiliza el espacio extra requiere un conocimiento más detallado de cómo el núcleo asigna la memoria (que es un conocimiento útil, por lo que investigaré y actualizaré más adelante).
ARG_MAX Confusión
Desde Linux 2.6.23 (como resultado de esta confirmación ), ha habido cambios en la forma en que se manejan los máximos de argumentos de comando, lo que hace que Linux difiera de otros sistemas similares a Unix. Además de agregar
MAX_ARG_STRLEN
yMAX_ARG_STRINGS
, el resultado degetconf ARG_MAX
ahora depende del tamaño de la pila y puede ser diferente deARG_MAX
inlimits.h
.Normalmente el resultado de
getconf ARG_MAX
será1/4
del tamaño de la pila. Considere lo siguiente albash
usarulimit
para obtener el tamaño de la pila:Sin embargo, el comportamiento anterior fue cambiado ligeramente por este commit (agregado en Linux 2.6.25-rc4 ~ 121).
ARG_MAX
enlimits.h
ahora sirve como un límite inferior duro en el resultado degetconf ARG_MAX
. Si el tamaño de la pila se establece de manera que1/4
el tamaño de la pila sea menor queARG_MAX
enlimits.h
, entonces selimits.h
usará el valor:Tenga en cuenta también que si el tamaño de la pila se establece por debajo del mínimo posible
ARG_MAX
, entonces el tamaño de la pila (RLIMIT_STACK
) se convierte en el límite superior del tamaño del argumento / entorno antes de queE2BIG
se devuelva (aunquegetconf ARG_MAX
todavía mostrará el valor enlimits.h
).Una última cosa a tener en cuenta es que si el núcleo se construye sin
CONFIG_MMU
(soporte para hardware de administración de memoria), entonces la comprobación deARG_MAX
está desactivada, por lo que el límite no se aplica. AunqueMAX_ARG_STRLEN
yMAX_ARG_STRINGS
todavía se aplican.Otras lecturas
ARG_MAX
valores (y equivalentes) en otros sistemas similares a Unix: http://www.in-ulm.de/~mascheck/various/argmax/MAX_ARG_STRLEN
causó un error en Automake que estaba incorporando scripts de shell en Makefiles usandosh -c
- http://www.mail-archive.com/[email protected]/msg05522.htmlfuente
En
eglibc-2.18/NEWS
En
eglibc-2.18/debian/patches/kfreebsd/local-sysdeps.diff
En
linux/include/uapi/linux/limits.h
Y
131072
es tu$(getconf ARG_MAX)/16-1
, quizás deberías comenzar en 0.Estás lidiando con glibc y Linux. Sería bueno parchear getconf también para obtener el
ARG_MAX
valor "correcto" devuelto.Editar:
Para aclarar un poco (después de una discusión corta pero caliente)
La
ARG_MAX
constante que se define enlimits.h
, da la longitud máxima de un argumento pasado con exec.El
getconf ARG_MAX
comando devuelve el valor máximo del tamaño de argumentos acumulados y el tamaño del entorno pasado a exec.fuente
eglibc-2.18/NEWS
fragmento? Sería bueno fijar esto en una versión particular del kernel.getconf ARG_MAX
trata del tamaño acumulativo de arg + env (variable en Linux reciente, veaulimit -s
y la otra pregunta que vinculé), no se trata de la longitud máxima de un solo argumento para el que no hay una consulta sysconf / getconf.Entonces @StephaneChazelas me corrige correctamente en los comentarios a continuación: el shell en sí no dicta de ninguna manera el tamaño máximo de argumento permitido por su sistema, sino que lo establece su núcleo.
Como ya han dicho varios otros, parece que el núcleo limita a 128 kb el tamaño máximo de argumento que puede pasar a un nuevo proceso desde cualquier otro cuando lo ejecute por primera vez. Experimenta este problema específicamente debido a las muchas
$(command substitution)
subcapas anidadas que deben ejecutarse en su lugar y entregar la totalidad de su salida de una a la siguiente.Y esta es una especie de suposición descabellada, pero como la discrepancia de ~ 5kb parece tan cercana al tamaño de página estándar del sistema, sospecho que está dedicada a los
bash
usos de la página para manejar la subshell que$(command substitution)
necesita para finalmente entregar su salida y / o la pila de funciones que emplea para asociar tuarray table
con tus datos. Solo puedo suponer que ninguno viene gratis.Demuestro a continuación que, si bien puede ser un poco complicado, es posible pasar valores de variables de shell muy grandes a nuevos procesos en la invocación, siempre que pueda lograr transmitirlos.
Para hacerlo, utilicé principalmente tuberías. Pero también evalué la matriz de shell en un
here-document
apuntado encat's stdin.
Resultados a continuación.Pero una última nota: si no tiene una necesidad particular de código portátil, me parece que
mapfile
podría simplificar un poco sus trabajos de shell.Posiblemente podría duplicar esto y luego volver a hacerlo si lo hizo en las transmisiones, no soy lo suficientemente mórbido como para descubrirlo, pero definitivamente funciona si lo transmite.
Intenté cambiar la
printf
parte del generador en la línea dos para:También funciona:
Entonces quizás soy un poco morbosa. Yo uso
zero padding here
y agrego el"$arg"
valor anterior al valor actual"$arg"
. Llego mucho más allá de 6500 ...Y si cambio la
cat
línea para que se vea así:Puedo obtener recuentos de bytes de
wc.
Recuerde que estos son los tamaños de cada clave en laargs
matriz. El tamaño total de la matriz es la suma de todos estos valores.fuente
echo $(tr -dc [:alnum:] </dev/urandom | head -c $(($(getconf ARG_MAX)*10))) >/dev/null
funcionará bien. Es solo cuando usa un comando externo que hay un problema.bash
comprimiendo de alguna manera?printf
es una orden interna por lo que no se ejecuta , y AFAICT, elcat
no se da ningún argumento.