Skip to content

Introdução

Juliano Santos edited this page Jan 22, 2019 · 119 revisions

O propósito do projeto bashsrc é manter o estilo de programação funcional do bash adicionando uma implementação genérica de tipos e métodos em paralelo com a programação pipe line padrão.

As bibliotecas disponíveis no projeto foram desenvolvidas em shell script utilizando apenas recursos builtindo interpretador de comandos bash ecoreutils. Cada biblioteca é composta por variáveis, tipos e funções que fornecem uma interface para manipulação de texto, regex, informações do sistema entre outras.

O projeto foi desenvolvido com o objetivo de ser escalável, ou seja, permitindo que o usuário crie suas próprias bibliotecas com funções, métodos e tipos de forma simples e eficiente.

O texto a seguir é um guia rápido e introdutório de como utilizar esses recursos e explorar suas funcionalidades.

Importando biblioteca

Para utilizar os recursos de uma biblioteca é necessário importá-la em seu script ou projeto por meio do comando source ou . seguido pelo nome.

Exemplo:

#!/bin/bash

source biblioteca.sh

ou

#!/bin/bash

. biblioteca.sh

A importação carrega na memória as funções, tipos e variáveis declaradas, disponibilizando-as em todo o projeto onde foram importadas.

builtin.sh

A biblioteca builtin é composta por funções básicas para declaração de tipos, iteração de elementos, loops e etc. Todavia é de suma importância e imprescindível para o funcionamento do eco sistema do bashsrc e cuja importação deve preceder qualquer outra biblioteca.

Ao contrário das demais bibliotecas, suas funções não possuem um prefixo, sendo referenciadas apenas por seu identificador curto.

Protótipo:

funcao

Obs: somente na consulta da documentação o prefixo builtin.funcao é requerido.

Exemplo:

Utilizando uma função builtin para converter um número inteiro para binário (base 2).

#!/bin/bash

# Importando
source builtin.sh

# função builtin
bin 255

Saída:

11111111

Funções

A biblioteca contém um conjunto de funções que realizam tarefas especificas onde a nomenclatura de cada função é prefixada pela 'biblioteca' a qual pertence (exceto builtin).

O protótipo de declaração determina a ordem e o tipo de cada argumento. Todos argumentos que compõe a função são obrigatórios e suporta um tipo especifico de dado, gerando uma mensagem de erro caso seja omitido ou inválido.

Protótipo:

funcao <arg1[tipo1]> <arg2[tipo2]> ... -> [tipo]
  |        |   |                    |       |
  |        |   |                    |       |___ Tipo do dado de retorno da função.
  |        |   |                    |
  |        |   |                    |__ argumentos variáticos (aceita um ou mais argumentos).
  |        |   |
  |        |   |__ Tipo do dado suporado pelo argumento. (tipo básico ou objeto)
  |        |
  |        |__ Nome do argumento.
  |
  |__ Identificador da função.

objeto é um tipo de dado definido pelo usuário.

Tipos básicos

O ecossitema do bashsrc suporta um conjunto de dados que são verificados na passagem de argumentos em funções ou estruturas cuja validação é realizada por ERE (expressão regular estentida) a nível de execução. São eles:

Tipo Descrição
uint Inteiro sem sinal.
int Inteiro com sinal.
float Número de precisão com sinal separado por '.' (ponto).
char Caractere único.
str Uma cadeia de caracteres.
bool Booleano (true ou false).
var Identificador de uma variável ou vetor válido.
array Array indexado. (inicializado)
map Array associativo. (inicializado)
function Nomenclatura de função válida.

Objetos

Além dos tipos básicos é possível definir tipos personalizados que podem implementar métodos ou estruturas. Por convenção é utilizado sufixos _t ou _st em nomenclaturas afim de especificar o tipo do dado.

Nomenclatura Descrição
var_t Tipo que implementa um ou mais métodos
var_st Tipo que implementa uma estrutura

Exemplo

O objeto string_t é um tipo que implementa todos os métodos que fazem referência as funções presentes na bibliioteca string.sh, fornecendo diferentes interfaces de programação.

Considere ambos os códigos abaixo que produzem o mesmo resultado.

#!/bin/bash

source string.sh

# Função
string.upper 'shell script'

ou

#!/bin/bash

source string.sh

# Implementação.
var texto string_t

# Atribui valor
texto='shell script'

# Método
texto.upper

Saída:

SHELL SCRIPT

Funções (retorno)

Diferentemente dos demais tipos o bool não retorna explicitamente um valor, mas, define o código de status da função após a execução, são eles:

Código Descrição
0 Sucesso
!= 0 Erro

Exemplo:

Utilizando a função str.compare para comparar duas strings e que retorna '0' se forem iguais ou '1' para diferentes.

# Comparando as strings.
str.compare "shell script" "SHELL SCRIPT"
echo $?

Saída:

1

Também é possível verificar o status da função executando-a diretamente em um bloco condicional if.

Exemplo:

if str.compare "shell" "shell"; then
    echo "Iguais"
else
    echo "Diferentes"
fi

ou

str.compare "shell" "shell" && echo "Iguais" || echo "Diferentes"

Saída:

Iguais

Função variádica:

É uma função que suporta um número indeterminado de argumentos a direita cuja declaração termina com .... Veja o protótipo abaixo:

function string.field <expr[str]> <sep[str]> <field[int]> ... [str]|[bool]

A função requer no mínimo 3 argumentos, sendo o último a direita variádico, ou seja, podendo ser especificado mais de um valor.

Chamadas válidas:

string.field 'f1,f2,f3,f4,f5' ',' 1
string.field 'f1,f2,f3,f4,f5' ',' 1 3 5
string.field 'f1,f2,f3,f4,f5' ',' {3..5}	# glob

Salvando retorno:

Para capturar o retorno de uma função é necessário chamá-la em um sub-shell utilizando o conjunto de expansão $(...).

var=$(funcao arg1 arg2 ...)

Exemplo:

#!/bin/bash

source string.sh

# Texto
texto='Seja livre, use Linux'

# Salvando retorno.
reverso=$(string.reverse "$texto")

# Imprimindo
echo "$reverso"

Saída:

xuniL esu ,ervil ajeS

Empilhamento:

O empilhamento é um conjunto de funções cujo o valor de retorno é tratado pela função externa subsequente.

Protótipo:

funcao1 "$(funcao2 "$(funcao 3)")"

Exemplo:

#!/bin/bash

source string.sh

# Texto
texto='Seja livre, use <so>'

# Converte o texto para maiúsculo antes de realizar a substituição.
string.replace "$(string.upper "$texto")" "<SO>" "LINUX" 1

Saída:

SEJA LIVRE, USE LINUX

Obs: a prioridade de expansão acontece de dentro para fora, ou seja, ocorre do sub-shell mais interno para o externo subsequente.

Função como argumento:

Uma função que recebe como argumento outra função que é executada a cada iteração e que recebe como argumento posicional $1 o elemento atual, anexando o valor de retorno da função de iteração.

Veja o protótipo da função fnmap da biblioteca builtin.sh.

                                                       (opcional)
                                                           |
                                 (função de iteração) [argumentos]
                                          |                |
function fnmap <iterable[array]> <funcname[function]> <[str]args> ...
                      |             
                  (variável)

A função fnmap lê o array e cada iteração chama funcname passando como argumento o elemento atual.

Exemplo:

#!/bin/bash

source string.sh

# Função
colunas()
{
    # Retorna o caractere entre '[...]'
    echo "[$1]"
}

string.fncmap 'SLACKWARE' colunas
string.fncmap 'SLACKWARE' string.repeat 3 # Passando argumentos.

Saída:

[S][L][A][C][K][W][A][R][E]
SSSLLLAAACCCKKKWWWAAARRREEE

Map

Um map é um array associativo que possui uma estrutura de dados composta por um conjunto de elementos referenciados por uma chave ou valor. As chaves podem ser definas pelo usuário e armazenadas na estrutura.

Para declarar um array associativo utilize os comandos local ou declare com parâmetro -A seguido pelo identificador da variável.

Exemplo:

local -A var=()
declare -A var=()

Consulte a documentação para mais detalhes: help declare ou help local.

Array

Array é uma estrutura de dados que armazena uma coleção de elementos que são referenciados por um índice, conhecida como array indexado.

Para declarar um array indexado utilize os comandos local ou declare com o parâmetro -a seguido pelo identificador da variável.

Exemplo:

local -a var=()
declare -a var=()

Uma variável também pode ser considerada como um array indexado podendo ser referenciada diretamente sem uma declaração explicita.

Exemplo:

var=(item1 item2 ...)
var[10]='item10'

Inicializando (array/map)

Funções com argumentos do tipo array ou map precisam ser previamente instanciadas antes passá-las na função.

Exemplo:

Considere o protótipo da função map.list que lista as chaves e valores do map especificado em name.

function map.list <[map]name> => [key|object]

Código:

/* INCORRETO */
# Não é um tipo 'map'
meu_map=''

map.list meu_map

---

/* CORRETO */
# Map
declare -A meu_map=()

map.list meu_map

Implementação de tipos (var)

Shell script não possui orientação a objetos, métodos ou heranças. Porém, como qualquer outra linguagem existe o escopo de visibilidade durante a declaração de uma variável e com a abstração adequada é possível a criação de funções que simulam métodos de acesso por entidade.

A função var é responsável por inicializar e implementar os métodos do objeto. Uma variável só pode ser implementada por um único objeto. (veja também: del)

Protótipo:

var varname ... object

varname nome da variável a ser implementada. 'object' nome do objeto de implementação.

Todo o objeto implementado é global, ou seja, seus métodos são visíveis por todo o código, exceto o valor da variável da entidade que só pode ser acessada dentro do seu escopo de declaração. (exceto struct_t)

Declarações:

/* CORRETO */
var v1 objeto
var v1 v2 v3 objeto
var {a..b} objeto

/* INCORRETO */
var 1v objeto
var 10 objeto

Exemplo:

#!/bin/bash

source builtin.sh
source string.sh

# Implementa os objetos.
var var_g string_t
var var_l string_t

var_p='Linux'

funcao(){
    # declara 'var_l' como local
    local var_l
    var_l='Windows'
}

# Chama 'funcao' para atribuir o valor de 'val_l'.
funcao

# Executa o método 'toupper' dos objetos implementados.
echo "var_g = $(var_g.upper)"
echo "val_l = $(var_l.upper)"

Saída:

var_p = LINUX
val_l = 

Note que apesar do método val_l.toupper ter sido executado com sucesso, nenhum valor foi retornado.

Método/Argumento posicional

Os métodos do objeto recebem o valor ou o identificador da variável implementada como argumento posicional $1 em sua chamada, dependendo do tipo da função pode ser por valor ou referência. De qualquer modo o método deve ser chamado omitindo o seu primeiro argumento.

Exemplo 1:

Veja o protótipo da função string.len que calcula o comprimento de uma string.

func string.len <exp[str]> => [uint]|[bool]
                    |
                    $1

Usando a função:

#!/bin/bash

source string.sh

# Usando a função
string.len "shell script"

Saída:

12

Usando o método:

#!/bin/bash

source builtin.sh
source string.sh

# Implementa texto com o tipo 'string_t'.
var texto string_t

# Armazena o texto
texto='shell script'

# Chama o método
texto.len

Saída:

12

Exemplo 2:

Utilizando a função string.repeat para repetir 'N' vezes uma determina string.

Protótipo:

function string.repeat <exp[str]> <count[uint]> -> [str]
                       |
                       $1

Usando a função:

#!/bin/bash

source builtin.sh
source string.sh

# Chama a função
texto.repeat 'LINUX-' 5

Saída:

LINUX-LINUX-LINUX-LINUX-LINUX-

Usando o método:

#!/bin/bash

source builtin.sh
source string.sh

# Implementa 'texto'.
var texto string_t

# Texto
texto='LINUX-'

# Chama o método
texto.repeat 5

Saída:

LINUX-LINUX-LINUX-LINUX-LINUX-

Estruturas

A implementação dos dados é feita de forma genérica por meio de métodos instanciados com nomenclaturas e tipos previamente definidos e referenciados em um map do objeto implementado para leitura e escrita.

Protótipo:

estrutura.__add__ membro1 tipo \
                  membro2 tipo \
                  membro3 tipo
ou

estrutura.__add__ membro1 tipo membro2 tipo membro3 tipo

tipo pode ser uma tipo básico ou objeto válido.

O objeto implementado por struct_t é global, ou seja, é visível em todo o código e pode ser acessado por seu identificador.

Exemplo:

#!/bin/bash

source builtin.sh
source struct.sh

# Implementando o tipo 'struct_t'
var objeto_t struct_t

# adicionando os membros da estrutura.
objeto_t.__add__ membro1 uint \
                 membro2 uint \
                 membro3 uint

# Implementando 'st' com 'objeto_t'
var st objeto_t

# Atribuindo valores
st.membro1 = 1
st.membro2 = 2
st.membro3 = 3

Leitura e gravação

O acesso ao membro da estrutura deve ser feito por referência ou atribuição.

Por Descrição
Referência Chamada direta ao membro da estrutura para leitura do dado armazenado.
Atribuição Atribui o valor a um membro especifico da estrutura e que deve ser precedido pelo operador '='.

Obs: deve conter um espaço inicial e final entre o operador de atribuição '='.

Protótipos:

estrutura.membro               # Referência.
estrutura.membro = "valor"     # Atribuição.

Composição

A composição de estruturas é quando um membro é instanciado como um tipo struct_t, herdando os membros da estrutura especificada.

Para realizar uma composição é necessário especificar o nome da estrutura que irá compor na passagem do tipo.

Protótipo:

struct.__add__ estrutura1 \
               membro1 estrutura2\
               membro2 estrutura3

Estrutura

estrutura1.membro1.estrutura2_membro
estrutura1.membro2.estrutura3_membro

Exemplo:

#!/bin/bash

source builtin.sh
source struct.sh

# Implementa as estruturas
var cliente_st local_st info_st struct_t

# info_st
info_st.__add__  sexo str \
				cor str \
                civil str

# local_st
local_st.__add__ endereco  str \
                cidade str \
                cep uint

# client_t
# Compõe as estruturas local_st e info_st.
cliente_st.__add__   nome str \
                    sobrenome str \
                    idade uint \
                    local local_st \
                    info info_st


# Implementa 'cliente' com o tipo 'client_st'
var cliente cliente_st

# Atribuindo valores
cliente.nome = 'Vanessa'
cliente.sobrenome = 'Oliveira'
cliente.idade = '42'

cliente.info.sexo = 'Feminino'
cliente.info.cor = 'Parda'
cliente.info.civil = 'Solteira'

cliente.local.endereco = 'Rua imaginária N 000 Apt -1'
cliente.local.cidade = 'Aiuruoca'
cliente.local.cep = '11111111'