La generazione di più budget paralleli e la memorizzazione dei risultati in un array bash da stampare piuttosto quando tutti i budget sono terminati


5

Ho un lungo elenco di URL sul mio sito Web elencati in un file di testo separato di ritorno a capo. Quindi per esempio:

  • http: /www.mysite.com/url1.html
  • http: /www.mysite.com/url2.html
  • http: /www.mysite.com/url3.html

Devo generare una serie di wget paralleli per colpire due volte ciascun URL, controllare e recuperare una determinata intestazione e quindi salvare i risultati in un array che voglio produrre in un bel report.

Ho parte di ciò che voglio usando il seguente comando xargs:

xargs -x -P 20 -n 1 wget --server-response -q -O - --delete-after<./urls.txt 2>&1 | grep Caching

La domanda è: come eseguire questo comando due volte e memorizzare quanto segue:

  1. L'URL ha colpito
  2. Il primo risultato del grep contro l'intestazione della cache
  3. Il 2 ° risultato del grep contro l'intestazione della cache

Quindi l'output dovrebbe assomigliare a:

=====================================================
http:/www.mysite.com/url1.html
=====================================================
First Hit: Caching: MISS
Second Hit: Caching: HIT

=====================================================
http:/www.mysite.com/url2.html
=====================================================
First Hit: Caching: MISS
Second Hit: Caching: HIT

E così via.

Ordinare che vengano visualizzati gli URL non è necessariamente un problema, purché le intestazioni siano associate all'URL.

A causa del numero di URL che devo colpire più URL in parallelo non in serie, altrimenti ci vorrà troppo tempo.

Il trucco è come ottenere più scommesse parallele E memorizzare i risultati in modo significativo. Non sono sposato con l'uso di un array se esiste un modo più logico di farlo (magari scrivendo in un file di registro?)

Qualche guru della bash ha qualche suggerimento su come procedere?


Le tue voci sono davvero separate dai ritorni a capo ( \r), non dalle nuove linee ( \n) o dallo stile di Windows (\r\n)? È un file di un vecchio Mac?
terdon,

1
Potresti voler sperimentare gnu parallel. in particolare la manpage menziona "Il parallelo GNU assicura che l'output dei comandi sia lo stesso che otterresti se avessi eseguito i comandi in sequenza".
Kampu,

Risposte:


3

Crea un piccolo script che fa la cosa giusta dato un singolo URL (basato sul codice di Terdon):

#!/bin/bash

url=$1
echo "=======================================";
echo "$url"
echo "=======================================";
echo -n "First Hit: Caching: ";
wget --server-response -q -O - $url 2>&1 | grep Caching >/dev/null
if [ $? == 0 ]; then echo HIT; else echo MISS; fi;
echo -n "Second Hit: Caching: ";      
wget --server-response -q -O - $url 2>&1 | grep Caching >/dev/null
if [ $? == 0 ]; then echo HIT; else echo MISS; fi; echo "";

Quindi eseguire questo script in parallelo (diciamo, 500 lavori alla volta) usando GNU Parallel:

cat urls.txt | parallel -j500 my_script

GNU Parallel si assicurerà che l'output di due processi non sia mai mischiato - una garanzia che xargs non fornisce.

Puoi trovare ulteriori informazioni su GNU Parallel su: http://www.gnu.org/s/parallel/

Puoi installare GNU Parallel in soli 10 secondi con:

wget -O - pi.dk/3 | sh 

Guarda il video introduttivo su http://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


1
Ah, sì, avrei dovuto pensarci, +1.
terdon,

0

Una banale soluzione sarebbe quella di registrare l'output di ciascuno dei wgetcomandi in un file separato e utilizzarlo catper unirli successivamente.


Ho 22.000 URL. Suppongo che potrei creare 22.000 file di testo e quindi provare a unirli ed eliminarli in seguito, ma devo ammettere che non sono terribilmente appassionato di generare tutto quell'I / O.
Brad

22.000 file non sono molto nel mio libro, ma immagino che provenga dal territorio. time for i in {1..22000}; do echo "Number $i" > $i; done- 1,7 secondi. Rimuovendoli: meno di un secondo.
l0b0

0

Presumo che il tuo file sia newline, non di ritorno a capo separato, perché il comando che dai non funzionerà con un \rfile separato.

Se il tuo file sta usando \rinvece che \nper le terminazioni di riga, modificalo in usando \neseguendo questo:

perl -i -pe 's/\r/\n/g' urls.txt 

Se si utilizzano i \r\nfinali di linea in stile Windows , utilizzare questo:

perl -i -pe 's/\r//g' urls.txt 

Ora, una volta che hai il tuo file in formato Unix, se non ti dispiace che i tuoi lavori non vengano eseguiti in parallelo , puoi fare qualcosa del genere:

while read url; do 
  echo "=======================================";
  echo "$url"
  echo "=======================================";
  echo -n "First Hit: Caching: ";
  wget --server-response -q -O - $url 2>&1 | grep Caching >/dev/null
  if [ $? == 0 ]; then echo HIT; else echo MISS; fi;
  echo -n "Second Hit: Caching: ";      
  wget --server-response -q -O - $url 2>&1 | grep Caching >/dev/null
  if [ $? == 0 ]; then echo HIT; else echo MISS; fi; echo "";
done < urls.txt

AGGIORNA in risposta al tuo commento:

Se hai 22.000 URL, posso davvero capire perché vuoi farlo in parallelo. Una cosa che potresti provare è la creazione di file tmp:

(while read url; do 
 ( 
  echo "=======================================";
  echo "$url"
  echo "=======================================";
  echo -n "First Hit: Caching: ";
  wget --server-response -q -O - $url 2>&1 | grep Caching >/dev/null
  if [ $? == 0 ]; then echo HIT; else echo MISS; fi;
  echo -n "Second Hit: Caching: ";      
  wget --server-response -q -O - $url 2>&1 | grep Caching >/dev/null
  if [ $? == 0 ]; then echo HIT; else echo MISS; fi; 
  echo ""; ) > `mktemp urltmpXXX` 2>/dev/null&
done < urls.txt )

Ci sono due subshells lanciati lì, il primo, (while ... < urls.txt)è proprio lì per sopprimere i messaggi di completamento . Il secondo ( ( echo "=== ... ) > mktemp urltmpXXX) è lì per raccogliere tutto l'output per un determinato URL in un file.

Lo script sopra creerà 22.000 file tmp chiamati urltmpXXXdove XXXviene sostituito da altrettanti caratteri casuali. Poiché i file tmp avranno ciascuno 6 righe di testo al termine di tutti, è quindi possibile monitorare (ed eventualmente eliminare i file) con questo comando:

b=`awk 'END{print NR}' urls.txt`; 
while true; do 
 a=`wc -l urltmp* | grep total | awk '{print $1}'`;     
 if [ $a == $((6 * $b)) ]; then cat urltmp* > urls.out; break; 
  else sleep 1; fi; 
done

Ora l'altro problema è che questo avvierà 22000 lavori contemporaneamente. A seconda del sistema in uso, questo potrebbe non essere un problema. Un modo per aggirare questo è il splittuo file di input e quindi eseguire il ciclo sopra una volta per ogni file.


Grazie ho già uno script che viene eseguito in serie. Vale a dire un URL alla volta. Il problema è che abbiamo 22.000 URL da colpire. Correre anche se in serie richiede troppo tempo. Ho bisogno di una soluzione che venga eseguita in parallelo per ridurre i tempi di esecuzione dello script. Il problema è che una volta eseguito in parallelo, come si registrano i risultati in un modo che può essere generato in un rapporto ragionevole in seguito?
Brad

@Brad Ho aggiornato la mia risposta con un modo (forse assurdamente contorto) di eseguirla in parallelo.
terdon,

In realtà questo ha messo in ginocchio il mio server. Oops! Immagino di aver bisogno di romperlo / strozzarlo in qualche modo.
Brad

@ Brad sì, ti avevo avvertito :). Provare a dividere il file in, diciamo 100 pezzi della linea: split -l 100 urls.txt, quindi eseguire il ciclo su ogni file: for file in x*; do (while read url; do ... ;done < $file); done. Qui, <$filesostituisce <urls.txt.
Terdon,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.