¿Cómo evitar que los xargs fusionen mal la salida de múltiples procesos?

17

Estoy usando xargscon la opción --max-args=0(alternativamente -P 0).

Sin embargo, la salida de los procesos se fusiona en la stdoutsecuencia sin tener en cuenta la separación de línea adecuada. Así que a menudo terminaré con líneas como:

<start-of-line-1><line-2><end-of-line-1>

Como estoy usando egrepcon ^mi patrón en toda la xargsproducción este es echar a perder mi resultado.

¿Hay alguna forma de obligar xargsa escribir las salidas del proceso en orden (cualquier orden, siempre que la salida de un proceso sea contigua)?

¿O alguna otra solución?

Editar: más detalles sobre el caso de uso:

Quiero descargar y analizar páginas web de diferentes hosts. Como cada página tarda aproximadamente un segundo en cargarse y hay unas pocas docenas de páginas, quiero paralelizar las solicitudes.

Mi comando tiene la siguiente forma:

echo -n $IPs | xargs --max-args=1 -I {} --delimiter ' ' --max-procs=0 \
wget -q -O- http://{}/somepage.html | egrep --count '^string'

Uso bash y no algo como Perl porque las IP del host (la variable $ IPs) y algunos otros datos provienen de un archivo bash incluido.

Christoph Wurm
fuente
¿Puedes dar un ejemplo más completo a tu pregunta? No está claro cómo o por qué está utilizando actualmente xargs.
Caleb
La solución a esto será difícil, uno necesita usar diferentes descriptores de archivo para stdout de cada proceso y usar un pequeño servidor para recopilar las líneas. xargsno parece proporcionar tal característica.
Stéphane Gimenez
@Caleb Ahí tienes, espero que esto ayude :-)
Christoph Wurm
Definitivamente no es una solución ligera, pero tal vez podría usar makela función de trabajos, creo que makecombina las líneas de salida correctamente.
Stéphane Gimenez
agrega la --line-bufferedbandera para egrepayudar
iruvar

Respuestas:

6

Esto debería funcionar:

echo -n $IPs | xargs --max-args=1 -I {} --delimiter ' ' --max-procs=0 \
  sh -c "wget -q -O- 'http://{}/somepage.html' | egrep --count '^string'" | \
  { NUM=0; while read i; do NUM=$(($NUM + $i)); done; echo $NUM; }

La idea aquí es hacer recuentos separados y sumarlos al final. Podría fallar si los recuentos por separado son lo suficientemente grandes como para ser mezclados, pero no debería ser el caso.

Stéphane Gimenez
fuente
14

GNU Parallel está específicamente diseñado para resolver este problema:

echo -n $IPs | parallel -d ' ' -j0 wget -q -O- http://{}/somepage.html | egrep --count '^string'

Si sus IP están en un archivo, es aún más bonito:

cat IPs | parallel -j0 wget -q -O- http://{}/somepage.html | egrep --count '^string'

Para obtener más información, vea el video de introducción: http://www.youtube.com/watch?v=OpaiGYxkSuQ

Ole Tange
fuente
2
Buena herramienta! Además, apuesto a que alguien te dirá que el gato es inútil muy pronto.
Stéphane Gimenez
1
Lo sé. Pero me resulta más fácil de leer, y generalmente trabajo en máquinas de 48 núcleos, por lo que los pocos ciclos de reloj adicionales para uno de los núcleos inactivos aún no han sido un problema.
Ole Tange
paralelo sería perfecto para el trabajo si estuviera en los repositorios de Debian.
Christoph Wurm
1
@Legate Debian incluye el parallelcomando de moreutils , que es suficiente aquí:parallel -j99 -i sh -c 'wget -q -O- http://{}/somepage.html | egrep -c "^string"' -- $IPs
Gilles 'SO- deja de ser malvado'
@Legate checkout build.opensuse.org/package/… para un archivo .deb y bugs.debian.org/cgi-bin/bugreport.cgi?bug=518696 para que el error lo elimine .
Ole Tange