Questão Gerando múltiplos alertas paralelos e armazenando resultados em uma matriz bash para ser bem impresso quando todos os desejos são feitos


Eu tenho uma longa lista de URLs no meu próprio site listado em um arquivo de texto separado de retorno de carro. Então, por exemplo:

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

Eu preciso gerar um número de wgets paralelos para acertar cada URL duas vezes, verificar e recuperar um cabeçalho específico e, em seguida, salvar os resultados em uma matriz que eu quero imprimir em um relatório legal.

Eu tenho parte do que eu quero usando o seguinte comando xargs:

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

A questão é como executar este comando duas vezes e armazenar o seguinte:

  1. O hit do URL
  2. O primeiro resultado do grep contra o cabeçalho do cache
  3. O 2º resultado do grep contra o cabeçalho do Cache

Então a saída deve ser algo como:

=====================================================
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 assim por diante.

Ordenar que os URLs sejam exibidos não é necessariamente uma preocupação, desde que os cabeçalhos estejam associados ao URL.

Devido ao número de URLs que preciso atingir vários URLs em paralelo, não em série, caso contrário, será muito demorado.

O truque é como obter vários alertas paralelos e armazenar os resultados de maneira significativa. Eu não sou casado com o uso de uma matriz se existe uma maneira mais lógica de fazer isso (talvez escrevendo em um arquivo de log?)

Algum guru bash tem alguma sugestão de como eu poderia proceder?


5


origem


Suas entradas são realmente separadas por retornos de carro (\r), não novas linhas (\n) ou estilo windows (\r\n)? Este é um arquivo de um velho Mac? - terdon
Você pode querer experimentar o gnu paralelo. em particular, a manpage menciona que "GNU parallel garante que a saída dos comandos é a mesma saída que você obteria se tivesse executado os comandos sequencialmente." - kampu


Respostas:


Faça um pequeno script que faça a coisa certa com um único URL (baseado no código de 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 "";

Em seguida, execute este script em paralelo (digamos, 500 jobs de cada vez) usando o GNU Parallel:

cat urls.txt | parallel -j500 my_script

O GNU Parallel fará com que a saída de dois processos nunca seja misturada - uma garantia que xargs não oferece.

Você pode encontrar mais sobre o GNU Parallel em: http://www.gnu.org/s/parallel/

Você pode instalar o GNU Parallel em apenas 10 segundos com:

wget -O - pi.dk/3 | sh 

Assista ao vídeo de introdução http://www.youtube.com/playlist?list=PL284C9FF2488BC6D1


3



Ah, sim, deveria ter pensado nisso, +1. - terdon


Uma solução trivial seria registrar a saída de cada um dos wget comandos para um arquivo separado e usando cat para mesclá-los depois.


0



Eu tenho 22.000 URLs. Eu suponho que eu poderia criar 22.000 arquivos de texto e, em seguida, tentar mesclar e excluí-los depois, mas devo admitir que não gosto muito de gerar todo esse I / O. - Brad
22.000 arquivos não são muito no meu livro, mas eu acho que vem com o território. time for i in {1..22000}; do echo "Number $i" > $i; done - 1,7 segundos Removendo-os: Menos de um segundo. - l0b0


Assumirei que seu arquivo é de nova linha, não de retorno de carro separado, porque o comando que você dá não funcionará com um \r arquivo separado.

Se o seu arquivo é usando \r ao invés de \n para fins de linha, altere-o para usar \n executando isto:

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

Se você estiver usando o estilo do Windows (\r\n) terminações de linha, use isto:

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

Agora, depois de ter seu arquivo no formato Unix, se você não se importa que seus trabalhos não sejam executados em paralelo, você pode fazer algo assim:

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

ATUALIZAR em resposta ao seu comentário:

Se você tem 22.000 URLs, posso realmente entender por que você quer fazer isso em paralelo. Uma coisa que você pode tentar é criar arquivos 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 )

Existem duas sub-unidades lançadas lá, a primeira (while ... < urls.txt) é justthere para suprimir mensagens de conclusão. O segundo (( echo "=== ... ) > mktemp urltmpXXX) está lá para coletar todas as saídas de um determinado URL em um arquivo.

O script acima criará 22.000 arquivos tmp chamados urltmpXXX onde o XXX é substituído por tantos caracteres aleatórios. Como os arquivos tmp terão 6 linhas de texto quando todos tiverem terminado, você poderá monitorar (e, opcionalmente, excluir os arquivos) com este 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

Agora, o outro problema é que isso vai lançar 22000 empregos de uma só vez. Dependendo do seu sistema, isso pode ou não ser um problema. Uma maneira de contornar isso é split seu arquivo de entrada e, em seguida, execute o loop acima uma vez para cada arquivo.


0



Obrigado, eu já tenho um script que é executado em série. I.E. um URL de cada vez. A questão é que temos 22.000 URLs para acertar. Correndo embora eles em série leva muito tempo. Eu preciso de uma solução que seja executada em paralelo para reduzir o tempo de execução do script. O problema é que, quando você executa em paralelo, como você registra os resultados de uma forma que pode ser gerada em um relatório sensato depois? - Brad
@Brad Atualizei minha resposta com uma maneira (talvez absurdamente complicada) de executá-la em paralelo. - terdon
Na verdade, isso deixou meu servidor de joelhos. Opa! Eu acho que eu preciso quebrar isso / acelerá-lo de alguma forma. - Brad
@Brad sim, eu avisei você :). Tente dividir o arquivo em, digamos, 100 trechos de linha: split -l 100 urls.txt, em seguida, execute o loop em cada arquivo: for file in x*; do (while read url; do ... ;done < $file); done. Aqui, <$file substitui <urls.txt. - terdon