Aprenda scripts Bash com multithreading usando o GNU Parallel

Se estás cansado dos teus scripts Bash a demorarem uma eternidade para correr, este tutorial é para ti. Muitas vezes, podes executar scripts Bash em paralelo, o que pode acelerar dramaticamente o resultado. Como? Utilizando a utilidade GNU Parallel, também chamada apenas de Parallel, com alguns exemplos úteis do GNU Parallel!

O Parallel executa scripts Bash em paralelo através de um conceito chamado multi-threading. Esta utilidade permite executar diferentes tarefas por CPU em vez de apenas uma, reduzindo o tempo para executar um script.

Neste tutorial, vais aprender a executar scripts Bash em multi-threading com toneladas de excelentes exemplos do GNU Parallel!

Pré-requisitos

Este tutorial estará repleto de demonstrações práticas. Se pretendes acompanhar, certifica-te de que tens o seguinte:

  • A Linux computer. Any distribution will work. The tutorial uses Ubuntu 20.04 running on Windows Subsystem for Linux (WSL).
  • Sessão iniciada com um utilizador com privilégios sudo.

Instalar o GNU Parallel

Para começar a acelerar os scripts Bash com multi-threading, primeiro tens de instalar o Parallel. Então, vamos começar por descarregar e instalá-lo.

1. Abre um terminal Bash.

2. Executa wget para descarregar o pacote Parallel. O comando abaixo descarrega a última versão (parallel-latest) para o diretório de trabalho atual.

wget https://ftp.gnu.org/gnu/parallel/parallel-latest.tar.bz2

Se preferir usar uma versão mais antiga do GNU Parallel, você pode encontrar todos os pacotes no site oficial de download.

3. Agora, execute o comando tar abaixo para descompactar o pacote que acabou de baixar.

Abaixo, o comando usa a bandeira x para extrair o arquivo, j para especificar que ele se destina a um arquivo com extensão .bz2, e f para aceitar um arquivo como entrada para o comando tar. sudo tar -xjf parallel-latest.tar.bz2

sudo tar -xjf parallel-latest.tar.bz2

Agora você deve ter um diretório chamado parallel- com o mês, dia e ano do último lançamento.

4. Navegue até a pasta do arquivo de arquivamento com cd. Neste tutorial, a pasta do arquivo de arquivamento é chamada de parallel-20210422, como mostrado abaixo.

Navigating to the Parallel archive folder

5. Em seguida, construa e instale o binário GNU Parallel executando os seguintes comandos:

./configure
 make
 make install

Agora, verifique se o Parallel foi instalado corretamente verificando a versão instalada.

parallel --version
Checking the GNU Parallel version

Ao executar o Parallel pela primeira vez, você também pode ver algumas linhas assustadoras que exibem texto como perl: warning:. Essas mensagens de aviso indicam que o Parallel não consegue detectar suas configurações atuais de localidade e idioma. Mas não se preocupe com esses avisos por enquanto. Você aprenderá como corrigir esses avisos mais tarde.

Configurando o GNU Parallel

Agora que o Parallel está instalado, você pode usá-lo imediatamente! No entanto, é importante configurar algumas configurações antes de começar.

Enquanto ainda estiver no seu terminal Bash, concorde com a permissão de pesquisa acadêmica do GNU Parallel research permission, informando ao Parallel que você o citará em qualquer pesquisa acadêmica, especificando o parâmetro citation seguido por will cite.

Se você não deseja apoiar o GNU ou seus mantenedores, concordar em citar não é obrigatório para usar o GNU Parallel.

parallel --citation
will cite

Altere a localidade definindo as seguintes variáveis de ambiente executando as linhas de código abaixo. Definir variáveis de ambiente de localidade e idioma como esta não é um requisito. Mas o GNU Parallel verifica a presença delas sempre que é executado.

Se as variáveis de ambiente não existirem, o Parallel reclamará sobre elas toda vez, como você viu na seção anterior.

Este tutorial assume que você é um falante de inglês. Outros idiomas são suportados também.

export LC_ALL=C man
export LANGUAGE=en_US
export LANG=en_US.UTF-8
Setting locale and language for GNU Parallel

Executando Comandos de Shell Ad-Hoc

Vamos agora começar a usar o GNU Parallel! Para começar, você aprenderá a sintaxe básica. Uma vez que esteja confortável com a sintaxe, você entrará em alguns exemplos úteis do GNU Parallel mais tarde.

Para começar, vamos abordar um exemplo super simples de simplesmente ecoar os números de 1 a 5.

1. No seu terminal Bash, execute os seguintes comandos. Empolgante, certo? O Bash usa o comando echo para enviar os números de 1 a 5 para o terminal. Se você colocasse cada um desses comandos em um script, o Bash executaria cada um sequencialmente, aguardando o término do anterior.

Neste exemplo, você está executando cinco comandos que não levam quase nenhum tempo. Mas, imagine se esses comandos fossem scripts Bash que realmente fizessem algo útil, mas levassem uma eternidade para serem executados?

 echo 1
 echo 2
 echo 3
 echo 4
 echo 5

Agora, execute cada um desses comandos ao mesmo tempo com o Parallel como abaixo. Neste exemplo, o Parallel executa o comando echo e, designado pelo :::, passa esse comando os argumentos, 1, 2, 3, 4, 5. Os três dois-pontos indicam ao Parallel que você está fornecendo entrada via linha de comando em vez do pipeline (mais sobre isso depois).

No exemplo abaixo, você passou um único comando para o Parallel sem opções. Aqui, como todos os exemplos do Parallel, o Parallel iniciou um novo processo para cada comando usando um núcleo de CPU diferente.

# A partir da linha de comando
 parallel echo ::: 1 2 3 4 5

Todos os comandos do Parallel seguem a sintaxe parallel [Opções] <Comando para multithreading>.

3. Para demonstrar o recebimento paralelo de entrada do pipeline Bash, crie um arquivo chamado count_file.txt como abaixo. Cada número representa o argumento que você passará para o comando echo.

 1
 2
 3
 4
 5

4. Agora, execute o comando cat para ler esse arquivo e passe a saída para o Parallel, conforme mostrado abaixo. Neste exemplo, o {} representa cada argumento (1-5) que será passado para o Parallel.

# A partir do pipeline cat count_file.txt | parallel echo {}
GNU Parallel Example #1

Comparando Bash e GNU Parallel

Agora, usar o Parallel pode parecer apenas uma maneira complicada de executar comandos Bash. Mas o benefício real para você é a economia de tempo. Lembre-se, o Bash será executado em apenas um núcleo da CPU, enquanto o GNU Parallel será executado em vários ao mesmo tempo.

1. Para demonstrar a diferença entre comandos sequenciais Bash vs. Parallel, crie um script Bash chamado test.sh com o seguinte código. Crie este script no mesmo diretório em que você criou o count_file.txt anteriormente.

O script Bash abaixo lê o arquivo count_file.txt, espera por 1, 2, 3, 4 e 5 segundos, exibe o comprimento da espera no terminal e termina.

#!/bin/bash
 nums=$(cat count_file.txt) # Ler count_file.txt
 for num in $nums           # Para cada linha no arquivo, iniciar um loop
 do
     sleep $num             # Ler a linha e esperar tantos segundos
     echo $num              # Imprimir a linha
 done

2. Agora, execute o script usando o comando time para medir quanto tempo o script leva para ser concluído. Levará 15 segundos.

time ./test.sh

3. Agora, use o comando time novamente para realizar a mesma tarefa, mas desta vez use o Parallel para fazê-lo.

O comando abaixo realiza a mesma tarefa, mas desta vez, em vez de esperar o primeiro loop ser concluído antes de iniciar o próximo, ele será executado em cada núcleo da CPU e iniciará o máximo possível ao mesmo tempo.

time cat count_file.txt | parallel "sleep {}; echo {}"
The prompt on the right side of the terminal confirms the time each command took to complete

Conheça o Teste de Execução!

Agora é hora de entrar em alguns exemplos mais práticos do GNU Parallel. Mas, antes de fazer isso, você deve primeiro conhecer a sinalização --dryrun. Esta sinalização é útil quando você deseja ver o que acontecerá sem o Parallel realmente fazê-lo.

A bandeira --dryrun pode ser a verificação final de sanidade antes de executar um comando que não se comporta como você pensava. Infelizmente, se você inserir um comando que possa prejudicar o seu sistema, a única coisa que o GNU Parallel irá ajudá-lo a fazer é prejudicá-lo mais rápido!

parallel --dryrun "rm rf {}"

Exemplo do GNU Parallel #1: Baixando Arquivos da Web

Para esta tarefa, você irá baixar uma lista de arquivos de várias URLs na web. Por exemplo, essas URLs podem representar páginas da web que você deseja salvar, imagens ou até mesmo uma lista de arquivos de um servidor FTP.

Para este exemplo, você vai baixar uma lista de pacotes de arquivo (e os arquivos SIG) do servidor FTP do GNU parallel.

1. Crie um arquivo chamado download_items.txt, pegue alguns links de download do site de download oficial e adicione-os ao arquivo separados por uma nova linha.

 https://ftp.gnu.org/gnu/parallel/parallel-20120122.tar.bz2
 https://ftp.gnu.org/gnu/parallel/parallel-20120122.tar.bz2.sig
 https://ftp.gnu.org/gnu/parallel/parallel-20120222.tar.bz2
 https://ftp.gnu.org/gnu/parallel/parallel-20120222.tar.bz2.sig

Você poderia economizar algum tempo usando a biblioteca Beautiful Soup do Python para extrair todos os links da página de download.

2. Leia todas as URLs do arquivo download_items.txt e passe-as para o Parallel, que invocará o wget e passará cada URL.

cat download_items.txt | parallel wget {}

Não se esqueça de que {} em um comando paralelo é um espaço reservado para a string de entrada!

3. Talvez você precise controlar o número de threads que o GNU Parallel usa de uma vez. Se for o caso, adicione o parâmetro --jobs ou -j ao comando. O parâmetro --jobs limita o número de threads que podem ser executadas simultaneamente para o número que você especificar.

Por exemplo, para limitar o Parallel a baixar cinco URLs de cada vez, o comando ficaria assim:

#!/bin/bash
 cat download_items.txt | parallel --jobs 5 wget {}

O parâmetro --jobs no comando acima pode ser ajustado para baixar qualquer número de arquivos, desde que o computador em que você está executando tenha CPUs suficientes para processá-los.

4. Para demonstrar o efeito do parâmetro --jobs, ajuste agora a contagem de trabalhos e execute o comando time para medir quanto tempo cada execução leva.

 time cat download_items.txt | parallel --jobs 5 wget {}
 time cat download_items.txt | parallel --jobs 10 wget {}

Exemplo do GNU Parallel #2: Descompactando Pacotes de Arquivos

Agora que você baixou todos esses arquivos de arquivamento do exemplo anterior, agora você deve descompactá-los.

Enquanto estiver no mesmo diretório dos pacotes de arquivos, execute o seguinte comando Parallel. Observe o uso do caractere curinga (*). Como este diretório contém tanto pacotes de arquivos quanto os arquivos SIG, você deve dizer ao Parallel para processar apenas os arquivos .tar.bz2.

sudo parallel tar -xjf ::: *.tar.bz2

Bônus! Se estiver usando o GNU parallel interativamente (não em um script), adicione a flag --bar para que o Parallel mostre uma barra de progresso enquanto a tarefa está em execução.

Showing the output from the --bar flag

Exemplo do GNU Parallel #3: Removendo Arquivos

Se você seguiu os exemplos um e dois, agora deve ter muitas pastas no seu diretório de trabalho ocupando espaço. Então vamos remover todos esses arquivos em paralelo!

Para remover todas as pastas que começam com parallel- usando o Parallel, liste todas as pastas com ls -d e encaminhe cada um dos caminhos das pastas para o Parallel, invocando rm -rf em cada pasta, como mostrado abaixo.

Lembre-se da flag --dryrun!

ls -d parallel-*/ | parallel "rm -rf {}"

Conclusão

Agora você pode automatizar tarefas com o Bash e economizar muito tempo. O que você escolhe fazer com esse tempo depende de você. Se economizar tempo significa sair um pouco mais cedo do trabalho ou ler outro post no blog ATA, é tempo de volta no seu dia.

Agora pense em todos os scripts de longa duração em seu ambiente. Quais deles você pode acelerar com o Parallel?

Source:
https://adamtheautomator.com/how-to-speed-up-bash-scripts-with-multithreading-and-gnu-parallel/