shell-script-pt
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [shell-script-pt] Paralelizando scripts com xargs


From: Julio C. Neves
Subject: Re: [shell-script-pt] Paralelizando scripts com xargs
Date: Thu, 25 Jun 2020 00:41:44 -0300

Vou mandar uma atualização do teu livro kkk Pela tanto que teorizei, que não é o meu estilo, dá pra ver que olhei por baixo do capô do Bash. Estava saíndo uma edição nova do livro qdo descobri que ele havia sido lançado junto com o Bash 4.0. Como anteriormente eu já havia analizado o código da substituição de processos, como o coproc é baseado nela e como no início não tinha nada, fui novamente no fonte para entender melhor e escrevi o seguinte:
==========================================

Coprocessos

A partir da versão 4.0, o bash incorporou o comando coproc. Este novo intrínseco (builtin) permite dois processos assíncronos se comunicarem e interagirem. Como cita Chet Ramey no Bash FAQ, ver. 4.01:

"Há uma nova palavra reservada, coproc, que especifica um coprocesso: um comando assíncrono que é executado com dois pipes conectados ao Shell criador. coproc pode receber nome. Os descritores dos arquivos de entrada e saída e o PID do coprocesso estão disponíveis para o Shell criador em variáveis com nomes específicos do coproc."

George Dimitriu explica:

"coproc é uma facilidade usada na Substituição de Processos que agora está publicamente disponível."

A comprovação do que disse Dimitriu não é complicada, quer ver? Veja a Substituição de Processos a seguir:

$ cat <(echo xxx; sleep 3; echo yyy; sleep 3)

Viu?! O cat não esperou pela conclusão dos comandos entre parênteses, mas foi executado no fim de cada um deles, isto é, a linha de comandos de cara listou xxx, dormiu 3 segundos, listou yyy, dormiu mais 3 segundos e terminou. Isso aconteceu porque se estabeleceu um pipe temporário/dinâmico e os comandos que estavam sendo executados mandavam para ele as suas saídas, que por sua vez as mandava para a entrada do cat.

Isso significa que os comandos dessa Substituição de Processos rodaram em paralelo, sincronizando somente nas saídas dos echo com a entrada do cat.

Se tivéssemos feito:

$ echo "$(echo xxx; sleep 3; echo yyy; sleep 3)"

Teríamos obtido o mesmo resultado, porém xxx e yyy seriam listados simultaneamente após 6 segundos, ou seja, os dados seriam mandados para a saída somente após o encerramento do comando, sem sincronização alguma.

Experimente fazer dessas duas formas que provavelmente você entenderá melhor.

A sintaxe de um coprocesso é:

coproc [NOME] CMD REDIR

Isso criará um coprocesso chamado NOME. Se NOME for informado, CMD deverá ser um comando composto. Caso contrário (no caso de NOME não ser informado), CMD poderá ser um comando simples ou composto.

Quando um coproc é executado, ele cria um vetor (array) com o mesmo nome NOME no Shell criador. Sua saída padrão é ligada via um pipe a um descritor de arquivo associado à variável ${NOME[0]} à entrada padrão do Shell pai (lembra que a entrada padrão de um processo é sempre associada por default ao descritor zero?). Da mesma forma, a entrada do coproc é ligada à saída padrão do script, via pipe, a um descritor de arquivos chamado ${NOME[1]}.

Assim, simplificando, vemos que o script mandará dados para o coproc pela variável ${NOME[0]} e receberá sua saída em ${NOME[1]}.

Note que esses pipes serão criados antes dos REDIR especificados pelo comando, já que eles serão as entradas e saídas do coprocesso.

A partir daqui, vou detalhar rapidamente uns estudos que fiz. Isso contém um pouco de divagações e muita teoria. Se você pular para depois desses ls's não perderá nada, mas, se acompanhar, pode ser bom para a compreensão do mecanismo do coproc.

Após colocar um coproc rodando, se ele estiver associado a um descritor de arquivo, vamos ver o que tem ativo no diretório correspondente:

$ ls -l /dev/fd

lrwxrwxrwx 1 root root 13 2010-01-06 09:31 /dev/fd -> /proc/self/fd

Hummm, é um link para o /proc/self/fd. O que será que tem lá?

$ ls -l /proc/self/fd

total 0

lrwx------ 1 julio julio 64 2010-01-06 16:03 0 -> /dev/pts/0

lrwx------ 1 julio julio 64 2010-01-06 16:03 1 -> /dev/pts/0

lrwx------ 1 julio julio 64 2010-01-06 16:03 2 -> /dev/pts/0

lr-x------ 1 julio julio 64 2010-01-06 16:03 3 -> /proc/3127/fd

Epa, que o 0, 1 e 2 apontavam para /dev/pts/0 eu já sabia, pois são a entrada padrão, saída padrão e saída de erros padrão apontando para o pseudo terminal corrente, mas quem será esse maldito device 3? Vejamos:

$ ls -l /proc/$$/fd $$ É o PID do Shell corrente

total 0

lr-x------ 1 julio julio 64 2010-01-06 09:31 0 -> /dev/pts/0

lrwx------ 1 julio julio 64 2010-01-06 09:31 1 -> /dev/pts/0

lrwx------ 1 julio julio 64 2010-01-06 09:31 2 -> /dev/pts/0

lrwx------ 1 julio julio 64 2010-01-06 16:07 255 -> /dev/pts/0

l-wx------ 1 julio julio 64 2010-01-06 16:07 54 -> pipe:[168521]

l-wx------ 1 julio julio 64 2010-01-06 16:07 56 -> pipe:[124461]

l-wx------ 1 julio julio 64 2010-01-06 16:07 58 -> pipe:[122927]

lr-x------ 1 julio julio 64 2010-01-06 16:07 59 -> pipe:[168520]

l-wx------ 1 julio julio 64 2010-01-06 16:07 60 -> pipe:[121302]

lr-x------ 1 julio julio 64 2010-01-06 16:07 61 -> pipe:[124460]

lr-x------ 1 julio julio 64 2010-01-06 16:07 62 -> pipe:[122926]

lr-x------ 1 julio julio 64 2010-01-06 16:07 63 -> pipe:[121301]

Epa, aí estão os links apontando para os pipes. Esse monte de arquivo de pipe que foi listado deve ser porque estava testando exaustivamente essa nova facilidade do Bash.

Para terminar essa teoria chata, falta dizer que o PID do Shell gerado para interpretar o coproc pode ser obtido na variável $NOME_PID e o comando wait pode ser usado para esperar pelo fim do coprocesso. O código de retorno do coprocesso ($?) é o mesmo de CMD, sendo NOME e CMD os mneumônicos que usei na sintaxe do comando.

Exemplos:

Vamos começar com o mais simples: um exemplo sem nome e direto no prompt:

$ coproc while read Entra coproc ativo

> do

> echo -=-=- $Entra -=-=-

> done

[2] 3030

$ echo Olá >&${COPROC[1]} Manda Olá para o pipe da saída

$ read -u ${COPROC[0]} Sai Lê do pipe da entrada para a variável Sai

$ echo $Sai

-=-=- Olá -=-=-

$ kill $COPROC_PID Isso não pode ser esquecido...

Como você viu, o vetor COPROC está associado a dois pipes; o ${COPROC[1]} que contém o endereço do pipe de saída, e por isso a saída do echo está redirecionada para ele, e o ${COPROC[0]} que contém o endereço do pipe de entrada, e por isso usamos a opção -u do read que lê dados a partir de um descritor de arquivo definido, em vez da entrada padrão.

Como o coprocesso utilizava a sintaxe sem NOME, o padrão do nome do vetor é COPROC.

Só mais uma teoriazinha chata:

$ echo ${COPROC[@]} Lista todos os elementos do vetor

59 54

Como você viu, ${COPROC[0]} estava usando o pipe apontado por /proc/$$/fd/59 e ${COPROC[1]} usava /proc/$$/fd/54.

Agora chega de teoria mesmo! Vamos usar NOME neste mesmo exemplo, para ver que pouca coisa muda:

$ coproc teste {

> while read Entra

> do

> echo -=-=- $Entra -=-=-

> done

> }

[6] 3192

$ echo Olá >&${teste[1]}

$ read -u ${teste[0]} Sai

$ echo $Sai

-=-=- Olá -=-=-

$ kill $teste_PID

Nesse momento, é bom mostrar uma coisa interessante: quais são os processos em execução?

$ ps Somente um Bash em execução

PID TTY TIME CMD

1900 pts/0 00:00:01 bash

2882 pts/0 00:00:00 ps

Vamos executar dois coprocessos simultâneos:

$ coproc nome1 { Coprocesso nome1

> while read x

> do

> echo $x

> done; }

[1] 2883

$ coproc nome2 { Coprocesso nome2

> while read y

> do

> echo $y

> done; }

bash: aviso: execute_coproc: coproc [2883:nome1] still exists

[2] 2884

Xiiii! Acho que deu zebra! Mas será que deu mesmo? Repare que, além do PID 2883 de nome1, ele também me devolveu o PID 2884, que deve ser de nome2. Vamos ver o que está acontecendo:

$ ps

PID TTY TIME CMD

1900 pts/0 00:00:01 bash Esse já existia

2883 pts/0 00:00:00 bash Esse está executando nome1

2884 pts/0 00:00:00 bash Esse está executando nome2

2885 pts/0 00:00:00 ps

Parece que foi só um aviso, pois os dois PIDs informados quando iniciamos os dois coprocessos estão ativos. Então vamos testar esses dois caras:

$ echo xxxxxxxxx >&${nome1[1]} Mandando cadeia para nome1

$ echo yyyyyyyyy >&${nome2[1]} Mandando cadeia para nome2

$ read -u ${nome1[0]} Recebe

$ echo $Recebe

xxxxxxxxx

$ read -u ${nome2[0]} Recebe

$ echo $Recebe

yyyyyyyyy

$ kill $nome1_PID

$ kill $nome2_PID

Abraços,
Julio

» Não tem tempo para fazer um curso presencial?
» Na sua cidade não tem nenhum bom curso de Linux?
Somente nesta semana de 15-19/06, as inscrições estarão
abertas para uma nova turma. Veja mais detalhes em:

Também damos treinamento em sua empresa
em qualquer cidadecom certificado e nota fiscal.







Em qua., 24 de jun. de 2020 às 21:38, Alfredo Casanova <atcasanova@gmail.com> escreveu:
Hahahaha apenas fatos, mestre!

Eu já dei uma olhada por cima no coproc e no parallel, mas achei os dois extremamente mais confusos que o xargs pra valer a pena parar pra aprender! 

Ainda está no "backlog"

Em qua, 24 de jun de 2020 19:42, Julio C. Neves <julio.neves@gmail.com> escreveu:
O artigo está ótimo, adorei. Dá uma olhada no coproc e vê se o paralelismo dele é mais ágil, não creio pq ele usa substituição de processos, mas não custa tentar.

Pô amigo o seu parágrafo:
"Se você fala português, essa é, sem a menor dúvida, a melhor forma possível de aprender shell-script disponível. E se você não fala português, vai ser mais fácil aprender português e fazer isso também, porque nunca encontrei nada melhor em inglês também."
Me comoveu de verdade... Muito obrigado.

Abraços,
Julio

» Não tem tempo para fazer um curso presencial?
» Na sua cidade não tem nenhum bom curso de Linux?
Somente nesta semana de 15-19/06, as inscrições estarão
abertas para uma nova turma. Veja mais detalhes em:

Também damos treinamento em sua empresa
em qualquer cidadecom certificado e nota fiscal.







Em qua., 24 de jun. de 2020 às 17:32, Alfredo Casanova <atcasanova@gmail.com> escreveu:
Pessoal,

tava explicando um script meu aqui pra um colega (na verdade, um script dele que editei e o tempo de execução saiu de 1m28s pra 9s) e resolvi tentar escrever um artigo pra explicar como fazer pra quem já tem algum conhecimento de bash. Não sei se ficou muito "informativo" ou acabou ficando confuso, portanto peço a crítica de vocês.


--
[]'s
Alfredo Tristão Casanova .͘.
Linux User #228230
tel: +55 61 9655 9619
_______________________________________________
Lista brasileira de usuários de shell script
Endereço de e-mail da lista: shell-script-pt@nongnu.org
Para se inscrever ou desinscrever acesse: https://lists.nongnu.org/mailman/listinfo/shell-script-pt
Para ver os arquivos da lista (mensagens anteriores) e pesquisar nelas, acesse https://lists.nongnu.org/archive/html/shell-script-pt/

NOTA: A lista anterior, no Yahoo Groups, foi *desativada*. Por favor utilize somente esta.
_______________________________________________
Lista brasileira de usuários de shell script
Endereço de e-mail da lista: shell-script-pt@nongnu.org
Para se inscrever ou desinscrever acesse: https://lists.nongnu.org/mailman/listinfo/shell-script-pt
Para ver os arquivos da lista (mensagens anteriores) e pesquisar nelas, acesse https://lists.nongnu.org/archive/html/shell-script-pt/

NOTA: A lista anterior, no Yahoo Groups, foi *desativada*. Por favor utilize somente esta.
_______________________________________________
Lista brasileira de usuários de shell script
Endereço de e-mail da lista: shell-script-pt@nongnu.org
Para se inscrever ou desinscrever acesse: https://lists.nongnu.org/mailman/listinfo/shell-script-pt
Para ver os arquivos da lista (mensagens anteriores) e pesquisar nelas, acesse https://lists.nongnu.org/archive/html/shell-script-pt/

NOTA: A lista anterior, no Yahoo Groups, foi *desativada*. Por favor utilize somente esta.

reply via email to

[Prev in Thread] Current Thread [Next in Thread]