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

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

Re: [shell-script] Re: Operação Bitwise em Bash


From: Julio C. Neves
Subject: Re: [shell-script] Re: Operação Bitwise em Bash
Date: Mon, 21 Oct 2019 13:43:58 -0300

Primeiro, um trecho do livro que falo em vc:
==========================================================================

Já tinha mandado a décima edição deste livro para a editora quando o Alfredo Casanova, colega da lista shell-script do Yahoo Groups, mandou a seguinte dica para a nossa lista:

Só compartilhando uma funçãozinha que fiz aqui para desenhar caixas de mensagem (só funciona para mensagens com uma linha. Se alguém quiser alterar, fique à vontade)

E nos brindou com esse código:

function DrawBox

{

string="$*";

tamanho=${#string}

tput setaf 4; printf "\e(0\x6c\e(B"

for i in $(seq $tamanho)

do printf "\e(0\x71\e(B"

done

printf "\e(0\x6b\e(B\n"; tput sgr0;

tput setaf 4; printf "\e(0\x78\e(B"

tput setaf 1; tput bold; echo -n $string; tput sgr0

tput setaf 4; printf "\e(0\x78\e(B\n"; tput sgr0;

tput setaf 4; printf "\e(0\x6d\e(B"

for i in $(seq $tamanho)

do printf "\e(0\x71\e(B"

done

printf "\e(0\x6a\e(B\n"; tput sgr0;

}

Seu uso seria da seguinte forma:

$ DrawBox Qualquer frase que caiba no terminal

Qualquer frase que caiba no terminal

Só que essa caixa é azul (tput setaf 4) e as letras são vermelhas, com ênfase (tput setaf 1; tput bold).

Mas observe a tabela a seguir. Ela explica os códigos gerados por esse monte de printf estranho:

O printf

Produz

\e(0\x6c\e(B

\e(0\x71\e(B

\e(0\x6b\e(B

\e(0\x78\e(B

\e(0\x6d\e(B

\e(0\x6a\e(B

Como eu tinha acabado de escrever os exemplos que vimos antes e ainda estava com eles na cabeça, sugeri que o for que ele usou para fazer as linhas horizontais fosse trocado; assim, substituiríamos:

for i in $(seq $tamanho)

do printf "\e(0\x71\e(B"

done

Por:

printf -v linha "%${tamanho}s" ' '

printf -v traco "\e(0\x71\e(B"

echo -n ${linha// /$traco}

Tudo Shell puro, pois o for, o printf e o echo são builtins, mas como o printf dentro do for seria executado tantas vezes quantos traços horizontais houvessem, imaginei que a minha solução fosse um pouco mais veloz e lhe pedi para testar os tempos de execução, não sem antes apostar um chope. Ele criou dois scripts, um que executava mil vezes a função que ele havia proposto e outro que fazia o mesmo com a minha solução. Não vou dizer qual foi a forma mais veloz, porém adianto que o Alfredo está me devendo um chope... ;)

O código otimizado ficaria assim:

function DrawBox

{

string="$*";

tamanho=${#string}

tput setaf 4; printf "\e(0\x6c\e(B"

printf -v linha "%${tamanho}s" ' '

printf -v traco "\e(0\x71\e(B"

echo -n ${linha// /$traco}

printf "\e(0\x6b\e(B\n"; tput sgr0;

tput setaf 4; printf "\e(0\x78\e(B"

tput setaf 1; tput bold; echo -n $string; tput sgr0

tput setaf 4; printf "\e(0\x78\e(B\n"; tput sgr0;

tput setaf 4; printf "\e(0\x6d\e(B"

printf -v linha "%${tamanho}s" ' '

printf -v traco "\e(0\x71\e(B"

echo -n ${linha// /$traco}

printf "\e(0\x6a\e(B\n"; tput sgr0;

}

Então agora, voltando aos nossos exemplos de passar uma linha por todo o terminal, posso sugerir uma outra (e mais bonita) solução:

printf -v linha "%$(tput cols)s" ' '

printf -v traco "\e(0\x71\e(B"

echo ${linha// /$traco}

Creio que quando resolvi explicar o printf através de exemplos acertei em cheio, pois não saberia como enumerar tantas regrinhas sem tornar a leitura enfadonha.

=======================================================================

Agora o material sobre o mapfile, que te prometi (repare que ele não menciona a opção que vc usou, em virtude dela só ter sido incrementada no bash 4.4):

Lendo um arquivo para um vetor

Ainda falando do bash 4.0, eis que ele surge com uma outra novidade: o comando intrínseco (builtin) mapfile, cuja finalidade é ler linhas da entrada primária (stdin) para dentro de um vetor indexado, sem fazer loop ou substituição de comando. Sua sintaxe é a seguinte:

mapfile [OPCS] [VETOR]

Onde os dados recebidos pela entrada primária irão para o vetor VETOR. Caso ele não seja especificado, a variável do sistema, MAPFILE, se incumbirá de receber esses dados.

As principais opções OPCS são:

Opção

Efeito

-n QTD

Copia, no máximo, QTD linhas

-O ORIGEM

Usa ORIGEM como o menor índice de VETOR

-s QTD

Descarta as primeiras QTD linhas

-c QTD

A cada QTD linhas lidas executa o que for especificado pela opção -c

-C COD

Executa o código Shell especificado em COD. O índice de VETOR é adicionado ao final

$ cat frutas Não confunda o arquivo com o vetor que usamos

abacate

maçã

morango

pera

tangerina

uva

$ mapfile vet < frutas Mandando frutas para vetor vet

$ echo ${vet[@]} Listando todos os elementos de vet

abacate maçã morango pera tangerina uva

Obteríamos resultado idêntico se fizéssemos:

$ vet=($(cat frutas))

Porém, isso seria mais lento porque a substituição de comando é executada em um subshell. Uma outra forma de fazer isso que logo vem à cabeça é ler o arquivo com a opção -a do comando read. Vamos ver como seria o comportamento disso:

$ read -a vet < frutas

$ echo ${vet[@]}

abacate

Como deu para perceber, foi lido somente o primeiro registro de frutas porque esse formato de read precisa receber todos os dados que serão lidos para o vetor vet. Para consertar isso poderíamos fazer:

$ read -a vet <<< $(cat frutas)

$ echo ${vet[@]}

abacate maçã morango pera tangerina uva

Mas aí a performance será pior até do que a anterior, pois também usa substituição de comandos e ainda por cima usa a instrução cat, que não é builtin.

Vejamos como funcionam as suas principais opções:

$ mapfile -n3 vet < frutas Para ler somente três linhas

$ echo ${vet[@]} Exibe os dados

abacate maçã morango

$ echo ${!vet[@]} Exibe os índices

0 1 2

$ unset vet "Mata" o vetor

$ mapfile vet < frutas Começando de novo

$ echo ${vet[@]}

abacate maçã morango pera tangerina uva

$ echo ${!vet[@]}

0 1 2 3 4 5

$ mapfile -O 6 vet < frutas Menor índice será o que já tinha mais 1

$ echo ${vet[@]}

abacate maçã morango pera tangerina uva abacate maçã morango pera tangerina uva

$ echo ${!vet[@]}

0 1 2 3 4 5 6 7 8 9 10 11

$ unset vet

$ mapfile -s 3 vet < frutas Despreza três linhas iniciais de frutas

$ echo ${vet[@]}

pera tangerina uva

$ echo ${!vet[@]}

0 1 2

$ mapfile -c1 -C"echo -n carreguei o índice" < frutas

carreguei o índice 0 abacate

carreguei o índice 1 maçã

carreguei o índice 2 morango

carreguei o índice 3 pera

carreguei o índice 4 tangerina

carreguei o índice 5 uva

O mesmo resultado seria obtido se fizesse:

$ mapfile -c1 -C"printf 'carreguei o índice %d %s'" < frutas

carreguei o índice 0 abacate

carreguei o índice 1 maçã

carreguei o índice 2 morango

carreguei o índice 3 pera

carreguei o índice 4 tangerina

carreguei o índice 5 uva

No entanto, poderíamos desprezar os índices, que são automaticamente lançados para a saída, se não especificarmos a máscara do printf para recebê-los (no caso %d e %s). Veja:

$ mapfile -c1 -C"printf 'Carreguei o índice'" < frutas

Carreguei o índiceCarreguei o índiceCarreguei o índiceCarreguei o índiceCarreguei o índiceCarreguei o índice

Epa, isso me deu uma ideia! Posso simular uma barra de progresso rústica usando essa técnica. Vejamos:

$ mapfile -c1 -C"printf '#'" < frutas

######$

Nesses últimos exemplos, por não termos especificado o nome do vetor, o Bash mandou todos os dados para um vetor do sistema chamado MAPFILE. Veja:

$ echo ${MAPFILE[@]}

abacate maçã morango pera tangerina uva

Isto é, foi lançado um jogo-da-velha (#) para cada elemento carregado no vetor. Isso é muito legal, mas se a quantidade de elementos for muito grande, a qualidade da apresentação será prejudicada. Para resolver isso, basta incrementar o valor da opção -c. Observe também que o prompt ficou colado na saída, sem saltar linha. Para resolver isso, o melhor é colocar um echo puro no final da linha. Vamos ver como carregar um vetor com duzentos elementos:

$ printf '%s\n' {1..200} | mapfile -c20 -C 'printf "#"'; echo

##########

=============================================================================

Abraços,
Julio

» Não tem tempo para fazer um curso presencial?
» Na sua cidade não tem nenhum bom curso de Linux?
» Em outubro abriremos uma semana de inscrições
» para uma nova turma. Veja mais detalhes em:

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







Em seg, 21 de out de 2019 às 12:26, Alfredo Casanova address@hidden [shell-script] <address@hidden> escreveu:
 

Julio, confirmei aqui e, de fato, o readarray é um alias pro mapfile

$ readarray --help
readarray: readarray [-d delim] [-n count] [-O origin] [-s count] [-t] [-u fd] [-C callback] [-c quantum] [array]
    Read lines from a file into an array variable.
   
    A synonym for `mapfile'.

On Sat, Oct 19, 2019 at 12:03 AM 'Julio C. Neves' address@hidden [shell-script] <address@hidden> wrote:
 

Acho que o readarray é um aliás do mapfile. Qdo ligar o computador, te mando o que escrevi sobre esse cmd.
Bom fim de semana

Em sex, 18 de out de 2019 12:08, Alfredo Casanova address@hidden [shell-script] <address@hidden> escreveu:
 

Eu li sobre a readarray essa semana, Júlio. Mas como fiz o script pra solucionar uma busca que precisamos aqui no trabalho e vai rodar num ambiente que eu controlo, não vi grandes problemas.. 
O redirect pro stderr eu vou colocar mesmo :)

Valeu!

On Fri, Oct 18, 2019 at 11:42 AM 'Julio C. Neves' address@hidden [shell-script] <address@hidden> wrote:
 

Troque:
echo "uso: ./$0 10.0.0.0/8 10.10.10.10/24"
por:
echo "uso: ./$0 10.0.0.0/8 10.10.10.10/24" >&2

Eu não usaria o readarray com a opção -d pq ele só foi implementada na última versão do bash (4.4) o que a torna incompatível com diversos ambientes.

Abraços,
Julio

» Não tem tempo para fazer um curso presencial?
» Na sua cidade não tem nenhum bom curso de Linux?
» Em outubro abriremos uma semana de inscrições
» para uma nova turma. Veja mais detalhes em:

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







Em qui, 17 de out de 2019 às 10:50, Alfredo Casanova address@hidden [shell-script] <address@hidden> escreveu:
 

Terminei o código (não sei se tá certo pq networking não é a minha MESMO, tive q pegar ajuda com vários amigos)
Nos testes que fiz, tá funcionando.
Se alguém quiser dar uma olhada (e corrigir, caso encontre erros), segue:



On Thu, Oct 17, 2019 at 10:18 AM 'Julio C. Neves' address@hidden [shell-script] <address@hidden> wrote:
 

Arkanon!!!!!! Que bom tê-lo de volta na lista!!! Nos conte como anda o LSD, pois tem tudo a ver com Shell.

A ideia era te cumprimentar, mas só para não sair do escopo da lista e já que vc falou em bc e [io]base, vou mostrar uma coisa, que por não ser muito conhecida, alguns pensam ser um erro do utilitário:

$ bc <<< "ibase=8; obase=16; 10+10"
12
$ bc <<< "obase=8; ibase=16; 10+10"
40

Isso ocorreu pq na 1a linha primeiro declaramos a input base como 8 e qdo declaramos a 2a como 16 ele já entendeu que esse 16 era na base 8, ou seja 14.

Conclusão: qdo for declarar a duas bases, declare 1o a de saída.
 
Abraços,
Julio

» Não tem tempo para fazer um curso presencial?
» Na sua cidade não tem nenhum bom curso de Linux?
» Em outubro abriremos uma semana de inscrições
» para uma nova turma. Veja mais detalhes em:

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







Em qua, 16 de out de 2019 às 19:52, Arkanon address@hidden [shell-script] <address@hidden> escreveu:
 

O bash considera números iniciando com 0 como na base octal:
$  echo $(( 01010 & 01010 ))
520

Se não começar com 0, considerará na base decimal:
$  echo $(( 1010 & 1010 ))
1010

Você pode indicar a base explicitamente:
$  echo $(( 8#1010 & 8#1010 ))
520
$  echo $(( 10#01010 & 10#01010 ))
1010

Portanto, se quer que seja considerada a base binária, indique-a:
$  echo $(( 2#00001010 & 2#00001010 ))
10
$  echo $(( 2#1010 & 2#1010 ))
10

Aliás, a base hexadecimal também tem sua sintaxe específica:
$  echo $(( 0x01010 & 0x01010 ))
4112
$  echo $(( 16#01010 & 16#01010 ))
4112

Observe que o resultado é dado na base decimal. Se quiser o resultado na mesma base de entrada, pode convertê-lo. Aqui tem algumas sugestões de procedimento:

$ dec=844
$ printf '%x\n' $dec # para hexadecimal
34c
$ printf '%X\n' $dec
34C
$ printf '%o\n' $dec # para octal
1514
$ bc <<< "obase=2;$dec" # para binario
1101001100  $ dec=844
$ bc <<< "obase=21;$dec" # para base 21 -> QUALQUER BASE
01 19 04

Att,

Em qua, 16 de out de 2019 às 19:25, Alfredo Casanova address@hidden [shell-script] <address@hidden> escreveu:
 

Pelo visto ele está fazendo bitwise comparando 1.100 com 1.010.

Como fugir disso?


Em qua, 16 de out de 2019 19:07, Alfredo Casanova <address@hidden> escreveu:
Galera, to fazendo um script aqui pra detectar se uma sub-rede menos abrangente está compreendida dentro de outra mais abrangente (exemplo: saber se a rede 10.100.100.128/25 está dentro da rede 10.100.100.0/24)

E aí no meio das operacoes de bitwise me peguei com um erro que nao consigo explicar:

$ echo $(( 11111111 & 11111111))
11111111
$ echo $(( 11111111 & 11111110))
11111110

Essas duas operações estão com comportamento adequado
Porém:

$ echo $(( 00001010 & 00001010 ))
520
$ echo $(( 00001010 & 00001100 ))
512

eu não faço ideia do q está acontecendo nesses casos.
Alguém ilumina?

--
[]'s
Alfredo Tristão Casanova .͘.
Linux User #228230
tel: +55 61 9655 9619



--
(o_  @arkanon  (Twitter)     __o
//\   address@hidden   _`\<,
V_/_      www.lsd.org.br  (_)/(_)
---------------------------------



--
[]'s
Alfredo Tristão Casanova .͘.
Linux User #228230
tel: +55 61 9655 9619



--
[]'s
Alfredo Tristão Casanova .͘.
Linux User #228230
tel: +55 61 9655 9619



--
[]'s
Alfredo Tristão Casanova .͘.
Linux User #228230
tel: +55 61 9655 9619


reply via email to

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