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.

```bash
#!/bin/bash

source string.sh

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

ou 

```bash
#!/bin/bash

source string.sh

# Implementação.
var texto string_t

# Atribui valor
texto='shell script'

# Método
texto.upper
```

**Saída:**

```bash
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.

```bash
# 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:**

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

ou

```bash
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:

```bash
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 `$(...)`.

```bash
var=$(funcao arg1 arg2 ...)
```

**Exemplo:**
```bash
#!/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:**
```bash
#!/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:**

```bash
#!/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:**
```bash
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:**
```bash
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:**
```bash
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:**
```bash
/* 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_t
```
> `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`)

**Exemplo:**

```bash
#!/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:**

```bash
#!/bin/bash

source string.sh

# Usando a função
string.len "shell script"
```
**Saída:**
```
12
```

**Usando o método:**

```bash
#!/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:**
```bash
#!/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:**

```bash
#!/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:**

```
/* CORRETO */
var nome_t struct_t
var nome_T struct_t
var _nome_t struct_t
var __nome1_nome2_T struct_t

/* ERRO */
var nome struct_t
var nome_ struct_t

```
> Obs: É retornada uma mensagem de erro se o objeto implementado por `struct_t` for um tipo já existente.

Após a declaração ele se torna um **objeto_t** que deve ser implementado para atribuição do valor dos membros da estrutura.

**Exemplo:**
```bash
#!/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:**
```bash
#!/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'
```

Clone this wiki locally