├── LICENSE ├── README.md ├── bashsrc.png ├── bin └── bashsrc └── src ├── array.sh ├── builtin.sh ├── cpu.sh ├── error.sh ├── getopt.sh ├── grp.sh ├── http.sh ├── json.sh ├── log.sh ├── map.sh ├── mem.sh ├── net.sh ├── os.sh ├── path.sh ├── ps.sh ├── rand.sh ├── regex.sh ├── setup.sh ├── ssh.sh ├── string.sh ├── struct.sh ├── textutil.fonts ├── textutil.sh ├── time.sh └── user.sh /README.md: -------------------------------------------------------------------------------- 1 | ## bashsrc 2.0.0 2 | 3 | ![](https://raw.githubusercontent.com/shellscriptx/bashsrc/master/bashsrc.png) 4 | 5 | ### Sobre 6 | 7 | O **bashsrc** é um projeto _open source_ distribuído em uma coleção de bibliotecas desenvolvidas em **shell script**, com um conjunto de funções úteis que fornece ao desenvolvedor um estilo de programação funcional com implementação de "tipos". 8 | 9 | O foco principal é a compatibilidade com o interpretador de comandos **BASH 4.3.0 (ou superior)**, cuja funções são desenvolvidas utilizando apenas recursos `built-in` e `coreutils`, evitando a utilização de dependências de pacotes externos que geram ‘coprocs’ durante a execução. Porém alguns critérios serão levados em consideração para tal aplicação: _desempenho, viabilidade, compatibilidade, distribuição da dependência_ entre outros, ficando de responsabilidade do desenvolvedor verificar e reportar tais dependências se houverem. 10 | 11 | ### Dependência 12 | 13 | |Pacote|Versão|Descrição| 14 | |-|-|-| 15 | |bash|4.3 (ou superior)|Interpretador de comandos BASH (Bourne-Again Shell).| 16 | 17 | ### Documentação 18 | 19 | A documentação padrão está disponível no arquivo fonte de cada biblioteca e que pode ser acessada pela utilitário [bashsrc](https://github.com/shellscriptx/bashsrc/wiki/Utilit%C3%A1rio) via linha de comando e distribuída junto ao projeto. 20 | 21 | 22 | **Para mais informações:** [clique aqui](https://github.com/shellscriptx/bashsrc/wiki) 23 | 24 | ### Reportando falhas 25 | 26 | * E-mail: shellscriptx@gmail.com 27 | 28 | ### Desenvolvedor 29 | 30 | * Juliano Santos [(SHAMAN)](https://t.me/x_SHAMAN_x) 31 | 32 | ### Comunidades 33 | 34 | * [Telegram (grupo)](https://t.me/shellscript_x) 35 | 36 | * [Facebook (fanpage)](https://fb.com/shellscriptx) 37 | 38 | * [Facebook (grupo)](https://fb.com/groups/shellscriptx) 39 | 40 | * [Blog](https://www.shellscriptx.blogspot.com.br) 41 | 42 | ### Gostaria de contribuir com o projeto? [clique aqui](https://www.padrim.com.br/apoiar?projeto_id=4707) 43 | 44 | ![padrim](https://s3-sa-east-1.amazonaws.com/padrimbucketteste/padrim/Logotipo_colorido_horizontal.png) 45 | -------------------------------------------------------------------------------- /bashsrc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shellscriptx/bashsrc/d3e80e590830fe6ae9c21570447313cf66d48f1e/bashsrc.png -------------------------------------------------------------------------------- /bin/bashsrc: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | # 20 | 21 | readonly BASENAME=${0##*/} 22 | readonly BASHSRC_VERSION=2.0.0 23 | 24 | if [ ! -v BASHSRC_PATH ]; then 25 | echo "$BASENAME: erro: 'BASHSRC_PATH' variável de ambiente não configurada." 1>&2 26 | exit 1 27 | fi 28 | 29 | # Ativa globbing 30 | shopt -s extglob 31 | set +f 32 | 33 | # Flags de informação. 34 | readonly -a IFLAGS=( 35 | NAME 36 | SYNOPSIS 37 | DESCRIPTION 38 | VERSION 39 | AUTHORS 40 | BUGS 41 | COPYRIGHT 42 | LINKS 43 | DEPENDS 44 | EMAILS 45 | PLATFORMS 46 | ) 47 | 48 | # Flags de ajuda. 49 | readonly -a HFLAGS=( 50 | FUNCTION 51 | TYPE 52 | STRUCT 53 | CONST 54 | MAP 55 | ARRAY 56 | ) 57 | 58 | # Regex/Grupos 59 | readonly -A RE=( 60 | [flag]='^\s*#+\s*\.' 61 | [nocomm]='^[^#]*$' 62 | [typedef]='^[^#]*\s*typedef\s+([a-zA-Z_][a-zA-Z0-9_]*)' 63 | [func]="${RE[flag]}(FUNCTION)\s+" 64 | [type]="${RE[flag]}(TYPE|STRUCT|CONST|MAP|ARRAY)\s+" 65 | ) 66 | 67 | function usage() 68 | { 69 | cat << _eof 70 | $BASENAME: ferramenta de ambiente e documentação. 71 | Uso: $BASENAME [OPÇÕES] 72 | 73 | Argumentos obrigatórios para opções longas também são para opções curtas. 74 | 75 | -l, --list - Lista os sources disponíveis em '\$BASHSRC_PATH'. 76 | -e, --env - Exibe o ambiente configurado em '\$BASHSRC_PATH'. 77 | -d, --doc [.|] - Exibe a documentação da biblioteca, função ou tipo. 78 | -h, --help - Exibe ajuda e sai. 79 | -c, --check-conflicts - Verifica se há conflito de tipos entre bibliotecas em '\$BASHSRC_PATH'. 80 | -v, --version - Exibe a versão e sai. 81 | 82 | Desenvolvido por: Juliano Santos [SHAMAN] 83 | Reportar falhas: 84 | Wiki: 85 | _eof 86 | } 87 | 88 | function list_sources() 89 | { 90 | local dir file 91 | local srcdir=${1:-$BASHSRC_PATH/src} 92 | 93 | for dir in "${srcdir[@]}"; do 94 | for file in "$dir/"*; do 95 | if [[ -d "$file" ]]; then 96 | list_sources "$file" 97 | else 98 | [[ $file =~ \.sh$ ]] && 99 | echo "$file" 100 | fi 101 | done 102 | done 103 | 104 | return 0 105 | } 106 | 107 | function view_doc() 108 | { 109 | local srcdir=$BASHSRC_PATH/src 110 | local hflags=${HFLAGS[@]} 111 | local iflags=${IFLAGS[@]} 112 | local srcname=${1##*/} 113 | local srcfile=${1%%.*}.sh 114 | local comp match help 115 | 116 | hflags=${RE[flag]}"(${hflags// /|})\s+(.+)$" 117 | iflags=${RE[flag]}"(${iflags// /|})$" 118 | 119 | if [[ ! $1 ]]; then 120 | printf '%s: doc: requer nome da biblioteca\n' "$BASENAME" 1>&2 121 | return 1 122 | elif [[ ! -e "$srcdir/$srcfile" ]]; then 123 | printf '%s: doc: "%s" biblioteca não encontrada\n' "$BASENAME" "$1" 1>&2 124 | return 1 125 | fi 126 | 127 | # Obtem nomenclatura composta. 128 | IFS='.' read _ comp <<< "$srcname" 129 | 130 | while read -r line; do 131 | if [[ $line =~ ${RE[nocomm]} ]]; then 132 | help= 133 | # Informação 134 | elif [[ ! $comp && $line =~ $iflags ]]; then 135 | match=true 136 | echo "${BASH_REMATCH[1]}" 137 | help=true 138 | continue 139 | # Ajuda 140 | elif [[ ! $comp && $line =~ $hflags ]]; then 141 | match=true 142 | echo "${BASH_REMATCH[1],,} ${BASH_REMATCH[2]}" 143 | continue 144 | # Funções e Tipos 145 | elif [[ $line =~ ${RE[func]}(${srcname#builtin.}.*)$ ]] || 146 | [[ $line =~ ${RE[type]}(${srcname#*.})$ ]]; then 147 | match=true 148 | help=true 149 | echo "${BASH_REMATCH[1],,} ${BASH_REMATCH[2]}" 150 | continue 151 | fi 152 | 153 | # Exibe detalhes do subitem caso seja especificado. 154 | [[ $help ]] && printf '%4s%s\n' '' "${line#+(#)}" 155 | 156 | # Define o status de retorno. 157 | # Se nehuma referência for encontrada status será igual à 'false'. 158 | ${match:-false} 159 | done < "$srcdir/$srcfile" || { 160 | # status: false 161 | printf "%s: doc: '%s' referência não encontrada\n" "$BASENAME" "$srcname" 1>&2 162 | return 1 163 | } 164 | 165 | return 0 166 | } 167 | 168 | check_conflicts() 169 | { 170 | local srcfile line err 171 | local -A srctypes 172 | 173 | # Lista todas as bibliotecas disponíveis. 174 | while read srcfile; do 175 | while read -r line; do 176 | # Trata a linha enquanto houver tipo definido (typedef) 177 | while [[ $line =~ ${RE[typedef]} ]]; do 178 | # Verifica se o tipo já existe, caso já exista imprime na saída 179 | # padrão as bibliotecas conflitantes. 180 | if [[ ${srctypes[${BASH_REMATCH[1]}]} ]]; then 181 | printf '== Conflito ==\n\nTipo: %s\n\nBiblioteca: %s\nBiblioteca: %s\n\n' \ 182 | "${BASH_REMATCH[1]}" \ 183 | "${srctypes[${BASH_REMATCH[1]}]}" \ 184 | "$srcfile" 185 | 186 | err=1 # status 187 | else 188 | # Anexa o tipo. 189 | srctypes[${BASH_REMATCH[1]}]=$srcfile 190 | fi 191 | line=${line/typedef+( )${BASH_REMATCH[1]}/} # Atualiza linha. 192 | done 193 | done < "$srcfile" # Biblioteca 194 | done < <(list_sources) 195 | 196 | return ${err:-0} 197 | } 198 | 199 | # Parâmetros. 200 | case $1 in 201 | -v|--version) printf '%s-%s\n' "$BASENAME" "$BASHSRC_VERSION";; 202 | -e|--env) printf 'BASHSRC_PATH=%s\nPATH=%s\n' "$BASHSRC_PATH" "$PATH";; 203 | -l|--list) list_sources;; 204 | -d|--doc) view_doc "$2";; 205 | -c|--check-conflicts) check_conflicts;; 206 | -h|--help) usage;; 207 | *) printf "Uso: %s [OPÇÕES]\nTente: '%s --help' para obter mais informações.\n" "$BASENAME" "$BASENAME";; 208 | esac 209 | 210 | exit $? 211 | 212 | # /* BASHSRC */ 213 | -------------------------------------------------------------------------------- /src/array.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __ARRAY_SH__ ] && return 0 21 | 22 | readonly __ARRAY_SH__=1 23 | 24 | source builtin.sh 25 | 26 | # .FUNCTION array.len -> [uint]|[bool] 27 | # 28 | # Retorna o total de elementos contidos no array. 29 | # 30 | function array.len() 31 | { 32 | getopt.parse 1 "obj:array:$1" "${@:2}" 33 | 34 | local -n __ref__=$1 35 | echo ${#__ref__[@]} 36 | return $? 37 | } 38 | 39 | # .FUNCTION array.append -> [bool] 40 | # 41 | # Anexa o elemento ao final do array. 42 | # 43 | function array.append() 44 | { 45 | getopt.parse 2 "obj:array:$1" "expr:str:$2" "${@:3}" 46 | 47 | local -n __ref__=$1 48 | __ref__+=("$2") 49 | return $? 50 | } 51 | 52 | # .FUNCTION array.clear -> [bool] 53 | # 54 | # Apaga todos os elementos do container. 55 | # 56 | function array.clear() 57 | { 58 | getopt.parse 2 "obj:array:$1" "${@:2}" 59 | 60 | unset $1 61 | return $? 62 | } 63 | 64 | # .FUNCTION array.clone -> [bool] 65 | # 66 | # Copia os elementos de origem para o container de destino 67 | # sobrescrevendo os elementos já existentes. 68 | # 69 | # == EXEMPLO == 70 | # 71 | # source array.sh 72 | # 73 | # arr1=(item1 item2 item3) 74 | # arr2=(item4 item5 item6) 75 | # 76 | # echo 'arr1 ->' ${arr1[@]} 77 | # echo 'arr2 ->' ${arr2[@]} 78 | # echo --- 79 | # 80 | # array.clone arr1 arr2 81 | # echo 'arr2 ->' ${arr2[@]} 82 | # 83 | # == SAÍDA == 84 | # 85 | # arr1 -> item1 item2 item3 86 | # arr2 -> item4 item5 item6 87 | # --- 88 | # arr2 -> item1 item2 item3 89 | # 90 | function array.clone() 91 | { 92 | getopt.parse 2 "src:array:$1" "dest:array:$2" "${@:3}" 93 | 94 | local -n __ref1__=$1 __ref2__=$2 95 | __ref2__=("${__ref1__[@]}") 96 | return $? 97 | } 98 | 99 | # .FUNCTION array.copy -> [bool] 100 | # 101 | # Anexa os elementos de origem no container de destino. 102 | # 103 | function array.copy() 104 | { 105 | getopt.parse 2 "src:array:$1" "dest:array:$2" "${@:3}" 106 | 107 | local -n __ref1__=$1 __ref2__=$2 108 | __ref2__+=("${__ref1__[@]}") 109 | return $? 110 | } 111 | 112 | # .FUNCTION array.count -> [uint]|[bool] 113 | # 114 | # Retorna a quantidade de ocorrências do elemento no container. 115 | # 116 | # == EXEMPLO == 117 | # 118 | # source array.sh 119 | # 120 | # arr=(item1 item2 item1 item1 item6 item7 item8) 121 | # array.count arr 'item1' 122 | # 123 | # == SAÍDA == 124 | # 125 | # 3 126 | # 127 | function array.count() 128 | { 129 | getopt.parse 2 "obj:array:$1" "expr:str:$2" "${@:3}" 130 | 131 | local -n __ref__=$1 132 | local __c__ __elem__ 133 | 134 | for __elem__ in "${__ref__[@]}"; do 135 | [[ $__elem__ == $2 ]] && ((++__c__)) 136 | done 137 | 138 | echo ${__c__:-0} 139 | 140 | return $? 141 | } 142 | 143 | # .FUNCTION array.items -> [str]|[bool] 144 | # 145 | # Retorna uma lista iterável dos elementos contidos no container. 146 | # 147 | function array.items() 148 | { 149 | getopt.parse 1 "obj:array:$1" "${@:2}" 150 | 151 | local -n __ref__=$1 152 | printf '%s\n' "${__ref__[@]}" 153 | 154 | return $? 155 | } 156 | 157 | # .FUNCTION array.index -> [int]|[bool] 158 | # 159 | # Retorna o índice do elemento no container. 160 | # 161 | function array.index() 162 | { 163 | getopt.parse 2 "obj:array:$1" "expr:str:$2" "${@:3}" 164 | 165 | local -n __ref__=$1 166 | local __ind__ __pos__ 167 | 168 | for __ind__ in ${!__ref__[@]}; do 169 | [[ ${__ref__[$__ind__]} == $2 ]] && 170 | __pos__=$__ind__ && 171 | break 172 | done 173 | 174 | echo ${__pos__:--1} 175 | 176 | return $? 177 | } 178 | 179 | # .FUNCTION array.insert -> [bool] 180 | # 181 | # Insere o elemento no índice do container, reidexando os elementos subsequentes 182 | # a partir do índice especificado. 183 | # 184 | # == EXEMPLO == 185 | # 186 | # source array.sh 187 | # 188 | # arr=(item1 item2 item4 item5) 189 | # echo ${arr[@]} 190 | # 191 | # array.insert arr 3 'item3' 192 | # echo ${arr[@]} 193 | # 194 | # == SAÍDA == 195 | # 196 | # item1 item2 item4 item5 197 | # item1 item2 item3 item4 item5 198 | # 199 | function array.insert() 200 | { 201 | getopt.parse 3 "obj:array:$1" "index:uint:$2" "expr:str:$3" "${@:4}" 202 | 203 | local -n __ref__=$1 204 | __ref__=("${__ref__[@]:0:$2}" [$2]="$3" "${__ref__[@]:$2}") 205 | 206 | return $? 207 | } 208 | 209 | # .FUNCTION array.pop -> [str]|[bool] 210 | # 211 | # Retorna e remove o elemento do indice especificado. Utilize notação negativa 212 | # para deslocamento reverso. 213 | # 214 | # == EXEMPLO == 215 | # 216 | # source array.sh 217 | # 218 | # arr=(item1 item2 item3 item4 item5) 219 | # 220 | # echo "arr ->" ${arr[@]} 221 | # echo --- 222 | # array.pop arr 0 # Primeiro 223 | # array.pop arr -1 # ùltimo 224 | # echo --- 225 | # echo "arr ->" ${arr[@]} 226 | # 227 | # == SAÍDA == 228 | # 229 | # arr -> item1 item2 item3 item4 item5 230 | # --- 231 | # item1 232 | # item5 233 | # --- 234 | # arr -> item2 item3 item4 235 | # 236 | function array.pop() 237 | { 238 | getopt.parse 2 "obj:array:$1" "index:int:$2" "${@:3}" 239 | 240 | local -n __ref__=$1 241 | 242 | echo "${__ref__[$2]}" 243 | unset __ref__[$2] 244 | return $? 245 | } 246 | 247 | # .FUNCTION array.remove -> [bool] 248 | # 249 | # Remove a primeira ocorrência do elemento. 250 | # 251 | function array.remove() 252 | { 253 | getopt.parse 2 "obj:array:$1" "expr:str:$2" "${@:3}" 254 | 255 | local -n __ref__=$1 256 | local __i__ 257 | 258 | for __i__ in ${!__ref__[@]}; do 259 | [[ ${__ref__[$__i__]} == $2 ]] && 260 | unset __ref__[$__i__] && break 261 | done 262 | 263 | return $? 264 | } 265 | 266 | # .FUNCTION array.reverse -> [bool] 267 | # 268 | # Inverte a ordem dos elementos. 269 | # 270 | # == EXEMPLO == 271 | # 272 | # source array.sh 273 | # 274 | # arr=(item1 item2 item3 item4 item5) 275 | # 276 | # echo ${arr[@]} 277 | # array.reverse arr 278 | # echo ${arr[@]} 279 | # 280 | # == SAÍDA == 281 | # 282 | # item1 item2 item3 item4 item5 283 | # item5 item4 item3 item2 item1 284 | # 285 | function array.reverse() 286 | { 287 | getopt.parse 1 "obj:array:$1" "${@:2}" 288 | 289 | local -n __ref__=$1 290 | 291 | mapfile -t $1 < <(printf '%s\n' "${__ref__[@]}" | tac) 292 | return $? 293 | } 294 | 295 | # .FUNCTION array.sort -> [str]|[bool] 296 | # 297 | # Define os elementos em uma ordem ascendente. 298 | # 299 | function array.sort() 300 | { 301 | getopt.parse 1 "obj:array:$1" "${@:2}" 302 | 303 | local -n __ref__=$1 304 | 305 | mapfile -t $1 < <(printf '%s\n' "${__ref__[@]}" | sort -d) 306 | return $? 307 | } 308 | 309 | # .FUNCTION array.join -> [str]|[bool] 310 | # 311 | # Retorna uma string que é a concatenação das strings no iterável 312 | # com o separador especificado. 313 | # 314 | function array.join() 315 | { 316 | getopt.parse 2 "obj:array:$1" "expr:str:$2" "${@:3}" 317 | 318 | local -n __ref__=$1 319 | local __tmp__ 320 | 321 | printf -v __tmp__ "%s${2//%/%%}" "${__ref__[@]}" 322 | echo "${__tmp__%$2}" 323 | 324 | return $? 325 | } 326 | 327 | # .FUNCTION array.item -> [str]|[bool] 328 | # 329 | # Retorna o elemento armazenado no índice especificado. 330 | # > Utilize notação negativa para deslocamento reverso. 331 | # 332 | function array.item() 333 | { 334 | getopt.parse 2 "obj:array:$1" "index:int:$2" "${@:3}" 335 | 336 | local -n __ref__=$1 337 | echo "${__ref__[$2]}" 338 | 339 | return $? 340 | } 341 | 342 | # .FUNCTION array.contains -> [bool] 343 | # 344 | # Retorna 'true' se contém o elemento, caso contrário 'false'. 345 | # 346 | function array.contains() 347 | { 348 | getopt.parse 2 "obj:array:$1" "expr:str:$2" "${@:3}" 349 | 350 | local -n __ref__=$1 351 | local __item__ 352 | 353 | for __item__ in "${__ref__[@]}"; do 354 | [[ $__item__ == $2 ]] && break 355 | done 356 | 357 | return $? 358 | } 359 | 360 | # .FUNCTION array.reindex -> [bool] 361 | # 362 | # Realiza a reindexação dos elementos. 363 | # 364 | function array.reindex() 365 | { 366 | getopt.parse 1 "obj:array:$1" "${@:2}" 367 | 368 | local -n __ref__=$1 369 | 370 | __ref__=("${__ref__[@]}") 371 | return $? 372 | } 373 | 374 | # .FUNCTION array.slice -> [str]|[bool] 375 | # 376 | # Retorna uma substring resultante do elemento dentro de um container. 377 | # O Slice é a representação do índice e intervalo que deve respeitar 378 | # o seguinte formato: 379 | # 380 | # [start:len]... 381 | # 382 | # start - Índice ou posição do elemento dentro do container. 383 | # len - comprimento a ser capturado a partir de 'start'. 384 | # 385 | # > Não pode conter espaços entre slices. 386 | # > Utilize notação negativa para deslocamento reverso. 387 | # 388 | # Pode ser especificado mais de um slice na expressão, onde o primeiro slice 389 | # representa a posição do elemento dentro do container e os slices subsequentes 390 | # o intervalo da cadeia de caracteres a ser capturada. 391 | # 392 | # == EXEMPLO == 393 | # 394 | # source array.sh 395 | # 396 | # arr=('Debian' 'Ubuntu' 'Manjaro' 'Fedora') 397 | # 398 | # array.slice arr '[0][:3]' # Os três primeiros caracteres do 1º elemento' 399 | # array.slice arr '[1][3:]' # Os três últimos caracteres do 2º elemento. 400 | # array.slice arr '[2][3:2]' # Os dois caracteres a partir da posição '3' do 3º elemento. 401 | # array.slice arr '[-1][:-2]' # O último elemento exceto os dois últimos caracteres. 402 | # array.slice arr '[:2]' # Os dois primeiros elementos. 403 | # 404 | # == SAÍDA == 405 | # 406 | # Deb 407 | # ntu 408 | # ja 409 | # Fedo 410 | # Debian 411 | # Ubuntu 412 | # 413 | function array.slice() 414 | { 415 | getopt.parse 2 "obj:array:$1" "slice:str:$2" "${@:3}" 416 | 417 | [[ $2 =~ ${__BUILTIN__[slice]} ]] || error.fatal "'$2' erro de sintaxe na expressão slice" 418 | 419 | local -n __ref__=$1 420 | local __slice__=$2 421 | local __arr__=("${__ref__[@]}") 422 | local __ini__ __len__ 423 | 424 | while [[ $__slice__ =~ \[([^]]+)\] ]]; do 425 | IFS=':' read __ini__ __len__ <<< ${BASH_REMATCH[1]} 426 | 427 | __ini__=${__ini__:-0} 428 | 429 | [[ ${BASH_REMATCH[1]} != *@(:)* ]] && __len__=1 430 | 431 | # array 432 | if [[ ${#__arr__[@]} -gt 1 ]]; then 433 | [[ $__len__ -lt 0 ]] && __arr__=() && break 434 | __len__=${__len__:-$((${#__arr__[@]}-$__ini__))} 435 | __arr__=("${__arr__[@]:$__ini__:$__len__}") 436 | else 437 | # string 438 | [[ ${__len__#-} -gt ${#__arr__} ]] && __arr__='' && break 439 | __len__=${__len__:-$((${#__arr__}-$__ini__))} 440 | __arr__=${__arr__:$__ini__:$__len__} 441 | fi 442 | __slice__=${__slice__/\[${BASH_REMATCH[1]}\]/} 443 | done 444 | 445 | printf '%s\n' "${__arr__[@]}" 446 | 447 | return $? 448 | } 449 | 450 | # .FUNCTION array.listindex -> [uint] 451 | # 452 | # Retorna o índice dos elementos. 453 | # 454 | function array.listindex() 455 | { 456 | getopt.parse 1 "obj:array:$1" "${@:2}" 457 | 458 | local -n __ref__=$1 459 | echo ${!__ref__[@]} 460 | return $? 461 | } 462 | 463 | # .FUNCTION array.list -> [uint|str]|[bool] 464 | # 465 | # Retorna uma lista iterável dos elementos e seus respectivos 466 | # indices no seguinte formato: 467 | # 468 | # index|item 469 | # 470 | function array.list() 471 | { 472 | getopt.parse 1 "obj:array:$1" "${@:2}" 473 | 474 | local -n __ref__=$1 475 | local __i__ 476 | 477 | for __i__ in ${!__ref__[@]}; do 478 | echo "$__i__|${__ref__[$__i__]}" 479 | done 480 | 481 | return $? 482 | } 483 | 484 | # .TYPE array_t 485 | # 486 | # Implementa o objeto 'S' com os métodos: 487 | # 488 | # S.len 489 | # S.append 490 | # S.clear 491 | # S.clone 492 | # S.copy 493 | # S.count 494 | # S.items 495 | # S.index 496 | # S.insert 497 | # S.pop 498 | # S.remove 499 | # S.reverse 500 | # S.sort 501 | # S.join 502 | # S.item 503 | # S.contains 504 | # S.reindex 505 | # S.slice 506 | # S.listindex 507 | # S.list 508 | # 509 | typedef array_t \ 510 | array.len \ 511 | array.append \ 512 | array.clear \ 513 | array.clone \ 514 | array.copy \ 515 | array.count \ 516 | array.items \ 517 | array.index \ 518 | array.insert \ 519 | array.pop \ 520 | array.remove \ 521 | array.reverse \ 522 | array.sort \ 523 | array.join \ 524 | array.item \ 525 | array.contains \ 526 | array.reindex \ 527 | array.slice \ 528 | array.listindex \ 529 | array.list 530 | 531 | readonly -f array.len \ 532 | array.append \ 533 | array.clear \ 534 | array.clone \ 535 | array.copy \ 536 | array.count \ 537 | array.items \ 538 | array.index \ 539 | array.insert \ 540 | array.pop \ 541 | array.remove \ 542 | array.reverse \ 543 | array.sort \ 544 | array.join \ 545 | array.item \ 546 | array.contains \ 547 | array.reindex \ 548 | array.slice \ 549 | array.listindex \ 550 | array.list 551 | 552 | # /* __ARRAY_SH__ */ 553 | -------------------------------------------------------------------------------- /src/cpu.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __CPU_SH__ ] && return 0 21 | 22 | readonly __CPU_SH__=1 23 | 24 | source builtin.sh 25 | 26 | # .FUNCTION cpu.getinfo -> [bool] 27 | # 28 | # Obtem informações do processador, 29 | # 30 | # == EXEMPLO == 31 | # 32 | # source cpu.sh 33 | # 34 | # # Inicializa o map 35 | # declare -A info=() 36 | # 37 | # # Obtendo informações. 38 | # cpu.getinfo info 39 | # 40 | # # Listando informações. 41 | # echo ${info[processor[0]]} 42 | # echo ${info[model_name[0]]} 43 | # echo ${info[cpu_mhz[0]]} 44 | # echo --- 45 | # echo ${info[processor[1]]} 46 | # echo ${info[model_name[1]]} 47 | # echo ${info[cpu_mhz[1]]} 48 | # 49 | # == SAÍDA == 50 | # 51 | # 0 52 | # Intel(R) Core(TM) i5-3330 CPU @ 3.00GHz 53 | # 1961.110 54 | # --- 55 | # 1 56 | # Intel(R) Core(TM) i5-3330 CPU @ 3.00GHz 57 | # 1875.432 58 | # 59 | function cpu.getinfo() 60 | { 61 | getopt.parse 1 "cpuinfo:map:$1" "${@:2}" 62 | 63 | local __flag__ __value__ __info__ 64 | local __i__=-1 65 | local -n __ref__=$1 66 | 67 | # Inicializar. 68 | __ref__=() || return 1 69 | 70 | while IFS=':' read __flag__ __value__; do 71 | __flag__=${__flag__//@($'\t')} 72 | __flag__=${__flag__// /_} 73 | __flag__=${__flag__,,} 74 | __value__=${__value__##+( )} 75 | case $__flag__ in 76 | processor) ((++__i__));; 77 | '') continue;; 78 | esac 79 | # Atribui o valor da chave. 80 | __ref__[$__flag__[$__i__]]=$__value__ 81 | done < /proc/cpuinfo || error.error "'/proc/cpuinfo' não foi possível ler o arquivo" 82 | 83 | return $? 84 | } 85 | 86 | # .MAP cpuinfo 87 | # 88 | # Chaves: 89 | # 90 | # address_sizes[N] 91 | # apicid[N] 92 | # bogomips[N] 93 | # bugs[N] 94 | # cache_alignment[N] 95 | # cache_size[N] 96 | # clflush_size[N] 97 | # core_id[N] 98 | # cpu_cores[N] 99 | # cpu_family[N] 100 | # cpuid_level[N] 101 | # cpu_mhz[N] 102 | # flags[N] 103 | # fpu[N] 104 | # fpu_exception[N 105 | # initial_apicid[N] 106 | # microcode[N] 107 | # model[N] 108 | # model_name[N] 109 | # physical_id[N] 110 | # power_management[N] 111 | # processor[N] 112 | # siblings[N] 113 | # stepping[N] 114 | # vendor_id[N] 115 | # wp[N] 116 | # 117 | # > 'N' é o índice do elemento. 118 | # 119 | 120 | readonly -f cpu.getinfo 121 | 122 | # /* _CPU_SH__ */ 123 | -------------------------------------------------------------------------------- /src/error.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __ERROR_SH__ ] && return 0 21 | 22 | readonly __ERROR_SH__=1 23 | 24 | source builtin.sh 25 | 26 | # .FUNCTION error.fatal -> [str]|[bool] 27 | # 28 | # Retorna uma mensagem de erro e finaliza o script com status '1'. 29 | # 30 | function error.fatal() 31 | { 32 | getopt.parse 1 "error:str:$1" "${@:2}" 33 | 34 | echo "erro: linha ${BASH_LINENO[-2]:--}: ${FUNCNAME[-2]:--}: ${1:-fatal}" 1>&2 35 | exit 1 36 | } 37 | 38 | # .FUNCTION error.fatalf ... -> [str]|[bool] 39 | function error.fatalf() 40 | { 41 | getopt.parse -1 "fmt:str:$1" "args:str:$2" ... "${@:3}" 42 | 43 | printf "erro: linha %d: %s: ${1:-fatal}" \ 44 | "${BASH_LINENO[-2]:--}" \ 45 | "${FUNCNAME[-2]:--}" \ 46 | "${@:2}" 1>&2 47 | 48 | exit 1 49 | } 50 | 51 | # .FUNCTION error.error -> [str]|[bool] 52 | function error.error() 53 | { 54 | getopt.parse 1 "error:str:$1" "${@:2}" 55 | 56 | echo "${FUNCNAME[-2]:--}: ${1:-error}" 1>&2 57 | return 1 58 | } 59 | 60 | # .FUNCTION error.errorf ... -> [str]|[bool] 61 | function error.errorf() 62 | { 63 | getopt.parse -1 "fmt:str:$1" "args:str:$2" ... "${@:3}" 64 | 65 | printf "${FUNCNAME[-2]:--}: ${1:-error}" "${@:2}" 1>&2 66 | return 1 67 | } 68 | 69 | # .FUNCTION error.warn -> [str]|[bool] 70 | function error.warn() 71 | { 72 | getopt.parse 1 "error:str:$1" "${@:2}" 73 | 74 | echo -e "\e[0;31m${FUNCNAME[-2]:--}: ${1:-warn}\e[0;m" 1>&2 75 | return 1 76 | } 77 | 78 | # .FUNCTION error.warnf ... -> [str]|[bool] 79 | function error.warnf() 80 | { 81 | getopt.parse -1 "error:str:$1" "args:str:$2" ... "${@:3}" 82 | 83 | printf "\e[0;31m${FUNCNAME[-2]:--}: ${1:-warn}\e[0;m" "${@:2}" 1>&2 84 | return 1 85 | } 86 | 87 | # .TYPE error_t 88 | # 89 | # Implementa o objeto 'S' com os métodos: 90 | # 91 | # S.fatal 92 | # S.fatalf 93 | # S.error 94 | # S.errorf 95 | # S.warn 96 | # S.warnf 97 | # 98 | typedef error_t \ 99 | error.fatal \ 100 | error.fatalf \ 101 | error.error \ 102 | error.errorf \ 103 | error.warn \ 104 | error.warnf 105 | 106 | readonly -f error.fatal \ 107 | error.fatalf \ 108 | error.error \ 109 | error.errorf \ 110 | error.warn \ 111 | error.warnf 112 | 113 | # /* __ERROR_SH__ */ 114 | -------------------------------------------------------------------------------- /src/getopt.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __GETOPT_SH__ ] && return 0 21 | 22 | readonly __GETOPT_SH__=1 23 | 24 | source builtin.sh 25 | 26 | # Protótipos. 27 | readonly -A __GETOPT__=( 28 | [parse]='\bgetopt\.parse\s-?[0-9]+\s"[^:]+:' 29 | [arg]=${__BUILTIN__[varname]} 30 | [type]=${__BUILTIN__[vartype]} 31 | [nargs]='^(-1|0|[1-9][0-9]*)$' 32 | [funcs]='^getopt\.(n?args|values?|types?|params)$' 33 | ) 34 | 35 | # Global 36 | declare -ag __GETOPT_PARSE__ 37 | 38 | # .FUNCTION getopt.parse ... -> [bool] 39 | # 40 | # Define e processa 'N' argumentos posicionais na linha de comando. 41 | # Retorna 'true' se todos os argumentos satisfazem os tipos estabelecidos, 42 | # caso contrário retorna 'false' e finaliza o script com status '1'. 43 | # 44 | # nargs - Número de argumentos suportados. 45 | # Se 'nargs = -1' ativa o suporte a argumentos variádicos. Utilize 46 | # '...' para determinar o argumento posicional variádico. 47 | # 48 | # O argumento passado na função é constituído por uma cadeia de caracteres 49 | # que determina o nome, tipo e valor do argumento posicional a ser 50 | # processado, e que precisa respeitar a seguinte sintaxe: 51 | # 52 | # arg:type:value 53 | # 54 | # arg - Nome do argumento posicional cujo caracteres suportados são: '[a-zA-Z0-9_]' 55 | # e deve iniciar com pelo menos uma letra ou underline '_'. 56 | # type - Tipo do dado suportado pelo argumento. O identificador deve ser um objeto 57 | # válido ou um tipo 'builtin'. 58 | # value - Valor do argumento posicional. 59 | # 60 | # Tipos (builtin): 61 | # 62 | # bool - true ou false. 63 | # str - Uma cadeia de caracteres. 64 | # char - Um caractere. 65 | # int - Inteiro. 66 | # uint - Inteiro positivo (sem sinal). 67 | # float - Números com valor de ponto flutuante. 68 | # var - Identificador de uma variável. 69 | # array - Identificador de um array válido. 70 | # map - Identificador de um array associativo válido. 71 | # function - Identificador de uma função válida. 72 | # 73 | # Exemplos: 74 | # 75 | # -- PADRÃO -- 76 | # 77 | # argumentos posicionais 78 | # | 79 | # ----------------- 80 | # | | 81 | # getopt.parse 2 "arg1:type1:$1" "arg2:type2:$2" "${@:3}" 82 | # | | 83 | # total argumentos 84 | # argumentos subsequentes 85 | # (total + 1) 86 | # 87 | # -- VARIÁDICA -- 88 | # 89 | # getopt.parse -1 "arg1:type1:$1" "arg2:type2:$2" ... "${@:3}" 90 | # | | | | | 91 | # variádica ---------------- | 92 | # | | 93 | # argumento argumentos 94 | # variádico subsequentes 95 | # 96 | # > Os argumentos subsequentes ao variático herdão seus atributos (exceto: valor). 97 | # 98 | function getopt.parse() 99 | { 100 | local arg args ntype type val param flag vparam vp fl 101 | 102 | [[ $1 =~ ${__GETOPT__[nargs]} ]] || error.fatal "'$1' número de argumentos inválido" 103 | [[ $1 -ge 0 && $((${#@}-1)) -gt $1 ]] && error.fatal "'${*:$1+2}' excesso de argumentos" 104 | 105 | # Limpa memória. (exceto: funções 'getopt') 106 | [[ ${FUNCNAME[1]} =~ ${__GETOPT__[funcs]} ]] || __GETOPT_PARSE__=() 107 | 108 | # Lê os argumentos posicionais. 109 | for param in "${@:2}"; do 110 | # Se a função conter argumentos variáticos, define o argumento atual 111 | # com os atributos do último argumento. 112 | [[ $vp ]] && param=$vparam:$param 113 | [[ $param == ... && $1 -eq -1 ]] && vp=1 && continue # Define a função como variática. 114 | 115 | IFS=':' read -r arg ntype val <<< "$param" 116 | 117 | [[ ! $vp && $arg == @($args) ]] && error.fatal "'$arg' conflito de argumentos" 118 | 119 | # Extrai tipo. 120 | type=${ntype%%[*} 121 | 122 | [[ $arg =~ ${__GETOPT__[arg]} ]] || error.fatal "'$arg' nome do argumento inválido" 123 | [[ $ntype =~ ${__GETOPT__[type]} ]] || error.fatal "'$ntype' erro de sintaxe" 124 | [[ $type == @(${__ALL_TYPE_NAMES__// /|}) ]] || error.fatal "'$type' tipo do objeto desconhecido" 125 | 126 | # Flag de validação. 127 | flag=${__BUILTIN_TYPES__[$type]} 128 | 129 | # Avalia o valor do argumento com base no tipo especificado. 130 | case $type in 131 | # Insira aqui os tipos especiais que requerem premissas de avaliação e critérios 132 | # que determinam o tipo do objeto. É dado como válido se o retorno de status da 133 | # última instrução for 'verdadeira', caso contrário uma mensagem de erro é 134 | # apresentada e a rotina finalizada com status 1. 135 | var) [[ $val =~ ${__BUILTIN__[vartype]} ]];; 136 | array) IFS=' ' read _ fl _ <<< $(declare -p $val 2>/dev/null); [[ $fl == *a* ]];; 137 | map) IFS=' ' read _ fl _ <<< $(declare -p $val 2>/dev/null); [[ $fl == *A* ]];; 138 | function) [[ $type == $(type -t $val) ]];; 139 | *) if [[ $flag && $val =~ $flag ]]; then : 140 | elif [[ $type == ${__OBJ_INIT__[${val:--}]%%|*} ]]; then 141 | [[ $ntype =~ ${__GETOPT__[type]} ]] 142 | [[ ! ${BASH_REMATCH[2]} && $(sizeof $val) -eq 0 ]] || 143 | [[ ${BASH_REMATCH[2]} == [] && $(sizeof $val) -gt 0 ]] || 144 | [[ ${BASH_REMATCH[3]} == $(sizeof $val) ]] 145 | else false 146 | fi 147 | ;; 148 | esac || error.fatal "<$arg[$ntype]>: '$val' tipo do dado inválido" 149 | 150 | # Salva a linha de comando. 151 | __GETOPT_PARSE__+=("$param") 152 | 153 | # Salva atributos. 154 | vparam=$arg:$type 155 | args+=${args:+|}${arg} 156 | done 157 | 158 | return $? 159 | } 160 | 161 | # .FUNCTION getopt.nargs -> [uint]|[bool] 162 | # 163 | # Retorna o total de argumentos 164 | # 165 | function getopt.nargs() 166 | { 167 | getopt.parse 0 "$@" 168 | 169 | echo ${#__GETOPT_PARSE__[@]} 170 | return $? 171 | } 172 | 173 | # .FUNCTION getopt.args -> [str]|[bool] 174 | # 175 | # Retorna o nome dos argumentos posicionais. 176 | # 177 | function getopt.args() 178 | { 179 | getopt.parse 0 "$@" 180 | 181 | local param arg 182 | for param in "${__GETOPT_PARSE__[@]}"; do 183 | IFS=':' read arg _ _ <<< "$param" 184 | echo "$arg" 185 | done 186 | return $? 187 | } 188 | 189 | # .FUNCTION getopt.types -> [str]|[bool] 190 | # 191 | # Retorna o tipo dos argumentos posicionais. 192 | # 193 | function getopt.types() 194 | { 195 | getopt.parse 0 "$@" 196 | 197 | local param type 198 | for param in "${__GETOPT_PARSE__[@]}"; do 199 | IFS=':' read _ type _ <<< "$param" 200 | echo "$type" 201 | done 202 | return $? 203 | } 204 | 205 | # .FUNCTION getopt.values -> [str]|[bool] 206 | # 207 | # Retorna o valor dos argumentos posicionais. 208 | # 209 | function getopt.values() 210 | { 211 | getopt.parse 0 "$@" 212 | 213 | local param val 214 | for param in "${__GETOPT_PARSE__[@]}"; do 215 | IFS=':' read -r _ _ val <<< "$param" 216 | echo "$val" 217 | done 218 | return $? 219 | } 220 | 221 | # .FUNCTION getopt.params -> [str|str]|[bool] 222 | # 223 | # Retorna uma string com os atributos dos argumentos 224 | # posicionais no seguinte formato: 225 | # 226 | # arg|type 227 | # 228 | function getopt.params() 229 | { 230 | getopt.parse 0 "$@" 231 | 232 | local param arg type 233 | for param in "${__GETOPT_PARSE__[@]}"; do 234 | IFS=':' read arg type _ <<< "$param" 235 | echo "$arg|$type" 236 | done 237 | return $? 238 | } 239 | 240 | # .FUNCTION getopt.value -> [str]|[bool] 241 | # 242 | # Retorna o valor do argumento especificado. 243 | # 244 | function getopt.value() 245 | { 246 | getopt.parse 1 "argname:str:$1" "${@:2}" 247 | 248 | local param arg val 249 | for param in "${__GETOPT_PARSE__[@]}"; do 250 | IFS=':' read -r arg _ val <<< "$param" 251 | [[ $arg == $1 ]] && echo "$val" 252 | done 253 | return $? 254 | } 255 | 256 | # .FUNCTION getopt.type -> [str]|[bool] 257 | # 258 | # Retorna o tipo do argumento especificado. 259 | # 260 | function getopt.type() 261 | { 262 | getopt.parse 1 "argname:str:$1" "${@:2}" 263 | 264 | local param arg type 265 | for param in "${__GETOPT_PARSE__[@]}"; do 266 | IFS=':' read arg type _ <<< "$param" 267 | [[ $arg == $1 ]] && echo "$type" 268 | done 269 | return $? 270 | } 271 | 272 | readonly -f getopt.parse \ 273 | getopt.nargs \ 274 | getopt.args \ 275 | getopt.types \ 276 | getopt.values \ 277 | getopt.params \ 278 | getopt.value \ 279 | getopt.type 280 | 281 | # /* __GETOPT_SH__ */ 282 | -------------------------------------------------------------------------------- /src/grp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __GRP_SH__ ] && return 0 21 | 22 | readonly __GRP_SH__=1 23 | 24 | source builtin.sh 25 | 26 | # .FUNCTION grp.getgroups -> [uint]|[bool] 27 | # 28 | # Retorna os IDs de grupo suplementares da chamada do processo. 29 | # 30 | function grp.getgroups() 31 | { 32 | getopt.parse 0 "$@" 33 | 34 | printf '%s\n' ${GROUPS[@]} 35 | return $? 36 | } 37 | 38 | # .FUNCTION grp.getgrall -> [str]|[bool] 39 | # 40 | # Retorna uma lista com todos os grupos do sistema. 41 | # 42 | function grp.getgrall() 43 | { 44 | getopt.parse 0 "$@" 45 | 46 | local grp 47 | 48 | while IFS=':' read grp _; do 49 | echo $grp 50 | done < /etc/group 51 | 52 | return $? 53 | } 54 | 55 | # .FUNCTION grp.getgrnam -> [bool] 56 | # 57 | # Obtém no banco de dados as informações do grupo especificado. 58 | # 59 | function grp.getgrnam() 60 | { 61 | getopt.parse 2 "name:str:$1" "grp:map:$2" "${@:3}" 62 | 63 | local __name__ __pass__ __gid__ __mem__ 64 | local -n __ref__=$2 65 | 66 | __ref__=() || return 1 67 | 68 | while IFS=':' read __name__ \ 69 | __pass__ \ 70 | __gid__ \ 71 | __mem__; do 72 | [[ $1 == $__name__ ]] && break 73 | done < /etc/group 74 | 75 | (($?)) && { error.error "'$1' grupo não encontrado"; return $?; } 76 | 77 | __ref__[gr_name]=$__name__ 78 | __ref__[gr_passwd]=$__pass__ 79 | __ref__[gr_gid]=$__gid__ 80 | __ref__[gr_mem]=$__mem__ 81 | 82 | return $? 83 | } 84 | 85 | # .FUNCTION grp.getgrgid -> [bool] 86 | # 87 | # Obtém no banco de dados as informações do id do grupo especificado. 88 | # 89 | function grp.getgrgid() 90 | { 91 | getopt.parse 2 "gid:uint:$1" "grp:map:$2" "${@:3}" 92 | 93 | local __name__ __pass__ __gid__ __mem__ 94 | local -n __ref__=$2 95 | 96 | __ref__=() || return 1 97 | 98 | while IFS=':' read __name__ \ 99 | __pass__ \ 100 | __gid__ \ 101 | __mem__; do 102 | [[ $1 == $__gid__ ]] && break 103 | done < /etc/group 104 | 105 | (($?)) && { error.error "'$1' gid não encontrado"; return $?; } 106 | 107 | __ref__[gr_name]=$__name__ 108 | __ref__[gr_passwd]=$__pass__ 109 | __ref__[gr_gid]=$__gid__ 110 | __ref__[gr_mem]=$__mem__ 111 | 112 | return $? 113 | } 114 | 115 | # .MAP grp 116 | # 117 | # Chaves: 118 | # 119 | # gr_name /* Nome do grupo */ 120 | # gr_passwd /* Senha criptgrafada do grupo ou vazio. */ 121 | # gr_gid /* Identificador númerico do grupo */ 122 | # gr_mem /* Lista de usuários ou membros do grupos separados por vírgula (,). */ 123 | # 124 | 125 | # Funções (somente leitura) 126 | readonly -f grp.getgroups \ 127 | grp.getgrall \ 128 | grp.getgrnam \ 129 | grp.getgrgid 130 | 131 | # /* __GRP_SH__ */ 132 | 133 | -------------------------------------------------------------------------------- /src/http.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __HTTP_SH__ ] && return 0 21 | 22 | readonly __HTTP_SH__=1 23 | 24 | source builtin.sh 25 | source struct.sh 26 | source setup.sh 27 | 28 | # Dependência. 29 | setup.package 'curl (>= 7.0)' 30 | 31 | readonly -A __HTTP__=( 32 | [methods]='@(GET|POST|HEAD|PUT|DELETE|CONNECT|OPTIONS|TRACE)' 33 | ) 34 | 35 | # .FUNCTION http.get -> [bool] 36 | # 37 | # Envia uma requisição ao servidor e salva a resposta no 38 | # mapa apontado por 'response'. 39 | # 40 | # == EXEMPLO == 41 | # 42 | # #!/bin/bash 43 | # 44 | # source http.sh 45 | # 46 | # # mapa 47 | # declare -A resp=() 48 | # 49 | # http.get 'ipecho.net/plain' resp 50 | # echo 'IP externo:' ${resp[body]} 51 | # 52 | # == SAÍDA == 53 | # 54 | # IP externo: 179.72.180.201 55 | # 56 | function http.get() 57 | { 58 | getopt.parse 2 "url:str:$1" "response:map:$2" "${@:3}" 59 | 60 | local __resp__ __header__ __value__ __bodyfile__ 61 | local -n __ref__=$2 62 | 63 | __ref__=() || return 1 64 | 65 | # Arquivo temporário que irá armazenar o contéudo da página. 66 | __bodyfile__=$(mktemp -qtu XXXXXXXXXXXXXXXXXXXX) 67 | trap "rm -f $__bodyfile__ 2>/dev/null" SIGINT SIGTERM SIGKILL SIGTSTP RETURN 68 | 69 | while IFS=$'\n' read -r __resp__; do 70 | # Lê os headers de resposta e mapeia as chaves 71 | # com a nomenclatura de cada header processado, 72 | # atribuindo os respectivos valores. 73 | if [ "${__resp__:0:1}" == '<' ]; then 74 | IFS=' ' read -r _ __header__ __value__ <<< "$__resp__" 75 | # Se estiver vazio lê o próximo 'header'. 76 | [[ ! $__value__ ]] && continue 77 | __header__=${__header__%:} 78 | __header__=${__header__,,} 79 | case $__header__ in 80 | http/1.@(0|1)) __ref__[proto]=$__header__ 81 | __ref__[status]=${__value__%% *};; 82 | *) __ref__[$__header__]=$__value__;; 83 | esac 84 | fi 85 | done < <(curl -qsvX GET "$1" --output $__bodyfile__ 2>&1) 86 | 87 | # Conteúdo. 88 | __ref__[body]=$(< $__bodyfile__) 89 | 90 | return $? 91 | } 92 | 93 | # .FUNCTION http.request -> [bool] 94 | # 95 | # Envia uma requisição ao servidor com o método HTTP a ser executado no recurso 96 | # com os campos e cabeçalhos especificados na estrutura 'headers' e salva a 97 | # resposta no mapa apontado por 'response'. 98 | # 99 | # HTTP (métodos): OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT 100 | # 101 | # == EXEMPLO == 102 | # 103 | # O exemplo a seguir utiliza um modelo simples para envio de mensagens 104 | # via API Telegram. 105 | # 106 | # #!/bin/bash 107 | # 108 | # source http.sh 109 | # 110 | # # API 111 | # url_api='https://api.telegram.org/bot/sendMessage' 112 | # 113 | # # Inicializa os arrays associativos. 114 | # declare -A fields=() 115 | # declare -A resp=() 116 | # 117 | # # Cabeçalho (estrutura) 118 | # var header http_header_st 119 | # 120 | # # Definindo configurações. 121 | # header.accept = 'application/json' 122 | # 123 | # # Campos para requisição do método da api. 124 | # fields[chat_id]=181885983 125 | # fields[text]='Enviando mensagem de teste' 126 | # 127 | # # Envia requisição para o servidor. 128 | # http.request 'POST' "$url_api" fields header resp 129 | # 130 | # # Retorno 131 | # echo "${resp[body]}" 132 | # 133 | # == SAÍDA == 134 | # 135 | # {"ok":true,"result":{"message_id":44471,"from":{"id":371714654,"is_bot":true,"first_name":"BASHBot","username":"bashxxx_bot"},"chat":{"id":181885983,"first_name":"SHAMAN","username":"x_SHAMAN_x","type":"private"},"date":1547900652,"text":"Enviando mensagem de teste"}} 136 | # 137 | function http.request() 138 | { 139 | getopt.parse 5 "method:str:$1" "url:str:$2" "fields:map:$3" "headers:http_header_st:$4" "response:map:$5" "${@:6}" 140 | 141 | local __field__ __opt__ __bodyfile__ __resp__ 142 | local __value__ __header__ __headers__ __re__ 143 | local -n __fields__=$3 __ref__=$5 144 | 145 | __ref__=() || return 1 146 | 147 | if [[ $1 != ${__HTTP__[methods]} ]]; then 148 | error.error "'$1' HTTP método inválido" 149 | return 1 150 | fi 151 | 152 | # Arquivo temporário que irá armazenar o contéudo da página. 153 | __bodyfile__=$(mktemp -qtu XXXXXXXXXXXXXXXXXXXX) 154 | trap "rm -f $__bodyfile__ 2>/dev/null" SIGINT SIGTERM SIGKILL SIGTSTP RETURN 155 | 156 | # Verifica se formato do contéudo a ser transmitido é um formulário 157 | # multipart para upload de arquivos e define a opção adequada. 158 | # -d (padrão) 159 | __re__='\bmultipart/form-data\b' 160 | [[ $($4.accept) =~ $__re__ ]] && __opt__='-F' 161 | 162 | # Cabeçalhos. 163 | __headers__=( 164 | [1]=$($4.aim) # a-im 165 | [2]=$($4.accept) # Accept 166 | [3]=$($4.accept_charset) # Accept-Charset 167 | [4]=$($4.accept_encoding) # Accept-Encoding 168 | [5]=$($4.accept_language) # Accept-Language 169 | [6]=$($4.accept_datetime) # Accept-Datetime 170 | [7]=$($4.accept_control_request_method) # Access-Control-Request-Method 171 | [8]=$($4.accept_control_request_headers) # Access-Control-Request-Headers 172 | [9]=$($4.authorization) # Authorization 173 | [10]=$($4.cache_control) # Cache-Control 174 | [11]=$($4.connection) # Connection 175 | [12]=$($4.content_length) # Content-Length 176 | [13]=$($4.content_md5) # Content-MD5 177 | [14]=$($4.content_type) # Content-Type 178 | [15]=$($4.cookie) # Cookie 179 | [16]=$($4.date) # Date 180 | [17]=$($4.expect) # Expect 181 | [18]=$($4.forwarded) # Forwarded 182 | [19]=$($4.from) # From 183 | [20]=$($4.host) # Host 184 | [21]=$($4.http2_settings) # HTTP2-Settings 185 | [22]=$($4.if_match) # If-Match 186 | [23]=$($4.if_modified_since) # If-Modified-Since 187 | [24]=$($4.if_none_match) # If-None-Match 188 | [25]=$($4.if_range) # If-Range 189 | [26]=$($4.if_unmodified_since) # If-Unmodified-Since 190 | [27]=$($4.max_forwards) # Max-Forwards 191 | [28]=$($4.origin) # Origin 192 | [29]=$($4.pragma) # Pragma 193 | [30]=$($4.proxy_authorization) # Proxy-Authorization 194 | [31]=$($4.range) # Range 195 | [32]=$($4.referer) # Referer 196 | [33]=$($4.te) # TE 197 | [34]=$($4.user_agent) # User-Agent 198 | [35]=$($4.upgrade) # Upgrade 199 | [36]=$($4.via) # Via 200 | [37]=$($4.warning) # Warning 201 | ) 202 | 203 | while IFS=$'\n' read -r __resp__; do 204 | # Lê os headers de resposta e mapeia as chaves 205 | # com a nomenclatura de cada header processado 206 | # atribuindo os respectivos valores. 207 | if [ "${__resp__:0:1}" == '<' ]; then 208 | IFS=' ' read -r _ __header__ __value__ <<< "$__resp__" 209 | # Se estiver vazio lê o próximo 'header'. 210 | [[ ! $__value__ ]] && continue 211 | __header__=${__header__%:} 212 | __header__=${__header__,,} 213 | case $__header__ in 214 | http/1.@(0|1)) __ref__[proto]=$__header__ 215 | __ref__[status]=${__value__%% *};; 216 | *) __ref__[$__header__]=$__value__;; 217 | esac 218 | fi 219 | done < <( 220 | # Converte o mapa contendo os campos em parâmetros de linha de comando 221 | # e envia ao comando 'curl' para processamento com os cabeçalhos definidos. 222 | for __field__ in "${!__fields__[@]}"; do 223 | echo "${__opt__:--d} $__field__='${__fields__[$__field__]}' " 224 | done | xargs -e curl -qsvX $1 "$2" \ 225 | ${__headers__[1]:+-H "A-IM: ${__headers__[1]}"} \ 226 | ${__headers__[2]:+-H "Accept: ${__headers__[2]}"} \ 227 | ${__headers__[3]:+-H "Accept-Charset: ${__headers__[3]}"} \ 228 | ${__headers__[4]:+-H "Accept-Encoding: ${__headers__[4]}"} \ 229 | ${__headers__[5]:+-H "Accept-Language: ${__headers__[5]}"} \ 230 | ${__headers__[6]:+-H "Accept-Datetime: ${__headers__[6]}"} \ 231 | ${__headers__[7]:+-H "Access-Control-Request-Method: ${__headers__[7]}"} \ 232 | ${__headers__[8]:+-H "Access-Control-Request-Headers: ${__headers__[8]}"} \ 233 | ${__headers__[9]:+-H "Authorization: ${__headers__[9]}"} \ 234 | ${__headers__[10]:+-H "Cache-Control: ${__headers__[10]}"} \ 235 | ${__headers__[11]:+-H "Connection: ${__headers__[11]}"} \ 236 | ${__headers__[12]:+-H "Content-Length: ${__headers__[12]}"} \ 237 | ${__headers__[13]:+-H "Content-MD5: ${__headers__[13]}"} \ 238 | ${__headers__[14]:+-H "Content-Type: ${__headers__[14]}"} \ 239 | ${__headers__[15]:+-H "Cookie: ${__headers__[15]}"} \ 240 | ${__headers__[16]:+-H "Date: ${__headers__[16]}"} \ 241 | ${__headers__[17]:+-H "Expect: ${__headers__[17]}"} \ 242 | ${__headers__[18]:+-H "Forwarded: ${__headers__[18]}"} \ 243 | ${__headers__[19]:+-H "From: ${__headers__[19]}"} \ 244 | ${__headers__[20]:+-H "Host: ${__headers__[20]}"} \ 245 | ${__headers__[21]:+-H "HTTP2-Settings: ${__headers__[21]}"} \ 246 | ${__headers__[22]:+-H "If-Match: ${__headers__[22]}"} \ 247 | ${__headers__[23]:+-H "If-Modified-Since: ${__headers__[23]}"} \ 248 | ${__headers__[24]:+-H "If-None-Match: ${__headers__[24]}"} \ 249 | ${__headers__[25]:+-H "If-Range: ${__headers__[25]}"} \ 250 | ${__headers__[26]:+-H "If-Unmodified-Since: ${__headers__[26]}"} \ 251 | ${__headers__[27]:+-H "Max-Forwards: ${__headers__[27]}"} \ 252 | ${__headers__[28]:+-H "Origin: ${__headers__[28]}"} \ 253 | ${__headers__[29]:+-H "Pragma: ${__headers__[29]}"} \ 254 | ${__headers__[30]:+-H "Proxy-Authorization: ${__headers__[30]}"} \ 255 | ${__headers__[31]:+-H "Range: ${__headers__[31]}"} \ 256 | ${__headers__[32]:+-H "Referer: ${__headers__[32]}"} \ 257 | ${__headers__[33]:+-H "TE: ${__headers__[33]}"} \ 258 | ${__headers__[34]:+-H "User-Agent: ${__headers__[34]}"} \ 259 | ${__headers__[35]:+-H "Upgrade: ${__headers__[35]}"} \ 260 | ${__headers__[36]:+-H "Via: ${__headers__[36]}"} \ 261 | ${__headers__[37]:+-H "Warning: ${__headers__[37]}"} \ 262 | --output $__bodyfile__ 2>&1 263 | ) 264 | 265 | # Conteúdo 266 | __ref__[body]=$(< $__bodyfile__) 267 | 268 | return $? 269 | } 270 | 271 | # .FUNCTION http.request_data -> [bool] 272 | # 273 | # Como 'http.request', porém envia uma string contendo uma estrutura de dados. 274 | # 275 | function http.request_data() 276 | { 277 | getopt.parse 5 "method:str:$1" "url:str:$2" "data:str:$3" "headers:http_header_st:$4" "response:map:$5" "${@:6}" 278 | 279 | local __field__ __opt__ __bodyfile__ __resp__ 280 | local __value__ __header__ __headers__ __re__ 281 | local -n __ref__=$5 282 | 283 | __ref__=() || return 1 284 | 285 | if [[ $1 != ${__HTTP__[methods]} ]]; then 286 | error.error "'$1' HTTP método inválido" 287 | return 1 288 | fi 289 | 290 | # Arquivo temporário que irá armazenar o contéudo da página. 291 | __bodyfile__=$(mktemp -qtu XXXXXXXXXXXXXXXXXXXX) 292 | trap "rm -f $__bodyfile__ 2>/dev/null" SIGINT SIGTERM SIGKILL SIGTSTP RETURN 293 | 294 | # Cabeçalhos. 295 | __headers__=( 296 | [1]=$($4.aim) # a-im 297 | [2]=$($4.accept) # Accept 298 | [3]=$($4.accept_charset) # Accept-Charset 299 | [4]=$($4.accept_encoding) # Accept-Encoding 300 | [5]=$($4.accept_language) # Accept-Language 301 | [6]=$($4.accept_datetime) # Accept-Datetime 302 | [7]=$($4.accept_control_request_method) # Access-Control-Request-Method 303 | [8]=$($4.accept_control_request_headers) # Access-Control-Request-Headers 304 | [9]=$($4.authorization) # Authorization 305 | [10]=$($4.cache_control) # Cache-Control 306 | [11]=$($4.connection) # Connection 307 | [12]=$($4.content_length) # Content-Length 308 | [13]=$($4.content_md5) # Content-MD5 309 | [14]=$($4.content_type) # Content-Type 310 | [15]=$($4.cookie) # Cookie 311 | [16]=$($4.date) # Date 312 | [17]=$($4.expect) # Expect 313 | [18]=$($4.forwarded) # Forwarded 314 | [19]=$($4.from) # From 315 | [20]=$($4.host) # Host 316 | [21]=$($4.http2_settings) # HTTP2-Settings 317 | [22]=$($4.if_match) # If-Match 318 | [23]=$($4.if_modified_since) # If-Modified-Since 319 | [24]=$($4.if_none_match) # If-None-Match 320 | [25]=$($4.if_range) # If-Range 321 | [26]=$($4.if_unmodified_since) # If-Unmodified-Since 322 | [27]=$($4.max_forwards) # Max-Forwards 323 | [28]=$($4.origin) # Origin 324 | [29]=$($4.pragma) # Pragma 325 | [30]=$($4.proxy_authorization) # Proxy-Authorization 326 | [31]=$($4.range) # Range 327 | [32]=$($4.referer) # Referer 328 | [33]=$($4.te) # TE 329 | [34]=$($4.user_agent) # User-Agent 330 | [35]=$($4.upgrade) # Upgrade 331 | [36]=$($4.via) # Via 332 | [37]=$($4.warning) # Warning 333 | ) 334 | 335 | while IFS=$'\n' read -r __resp__; do 336 | # Lê os headers de resposta e mapeia as chaves 337 | # com a nomenclatura de cada header processado 338 | # atribuindo os respectivos valores. 339 | if [ "${__resp__:0:1}" == '<' ]; then 340 | IFS=' ' read -r _ __header__ __value__ <<< "$__resp__" 341 | # Se estiver vazio lê o próximo 'header'. 342 | [[ ! $__value__ ]] && continue 343 | __header__=${__header__%:} 344 | __header__=${__header__,,} 345 | case $__header__ in 346 | http/1.@(0|1)) __ref__[proto]=$__header__ 347 | __ref__[status]=${__value__%% *};; 348 | *) __ref__[$__header__]=$__value__;; 349 | esac 350 | fi 351 | done < <(curl -qsvX $1 "$2" -d "$3" \ 352 | ${__headers__[1]:+-H "A-IM: ${__headers__[1]}"} \ 353 | ${__headers__[2]:+-H "Accept: ${__headers__[2]}"} \ 354 | ${__headers__[3]:+-H "Accept-Charset: ${__headers__[3]}"} \ 355 | ${__headers__[4]:+-H "Accept-Encoding: ${__headers__[4]}"} \ 356 | ${__headers__[5]:+-H "Accept-Language: ${__headers__[5]}"} \ 357 | ${__headers__[6]:+-H "Accept-Datetime: ${__headers__[6]}"} \ 358 | ${__headers__[7]:+-H "Access-Control-Request-Method: ${__headers__[7]}"} \ 359 | ${__headers__[8]:+-H "Access-Control-Request-Headers: ${__headers__[8]}"} \ 360 | ${__headers__[9]:+-H "Authorization: ${__headers__[9]}"} \ 361 | ${__headers__[10]:+-H "Cache-Control: ${__headers__[10]}"} \ 362 | ${__headers__[11]:+-H "Connection: ${__headers__[11]}"} \ 363 | ${__headers__[12]:+-H "Content-Length: ${__headers__[12]}"} \ 364 | ${__headers__[13]:+-H "Content-MD5: ${__headers__[13]}"} \ 365 | ${__headers__[14]:+-H "Content-Type: ${__headers__[14]}"} \ 366 | ${__headers__[15]:+-H "Cookie: ${__headers__[15]}"} \ 367 | ${__headers__[16]:+-H "Date: ${__headers__[16]}"} \ 368 | ${__headers__[17]:+-H "Expect: ${__headers__[17]}"} \ 369 | ${__headers__[18]:+-H "Forwarded: ${__headers__[18]}"} \ 370 | ${__headers__[19]:+-H "From: ${__headers__[19]}"} \ 371 | ${__headers__[20]:+-H "Host: ${__headers__[20]}"} \ 372 | ${__headers__[21]:+-H "HTTP2-Settings: ${__headers__[21]}"} \ 373 | ${__headers__[22]:+-H "If-Match: ${__headers__[22]}"} \ 374 | ${__headers__[23]:+-H "If-Modified-Since: ${__headers__[23]}"} \ 375 | ${__headers__[24]:+-H "If-None-Match: ${__headers__[24]}"} \ 376 | ${__headers__[25]:+-H "If-Range: ${__headers__[25]}"} \ 377 | ${__headers__[26]:+-H "If-Unmodified-Since: ${__headers__[26]}"} \ 378 | ${__headers__[27]:+-H "Max-Forwards: ${__headers__[27]}"} \ 379 | ${__headers__[28]:+-H "Origin: ${__headers__[28]}"} \ 380 | ${__headers__[29]:+-H "Pragma: ${__headers__[29]}"} \ 381 | ${__headers__[30]:+-H "Proxy-Authorization: ${__headers__[30]}"} \ 382 | ${__headers__[31]:+-H "Range: ${__headers__[31]}"} \ 383 | ${__headers__[32]:+-H "Referer: ${__headers__[32]}"} \ 384 | ${__headers__[33]:+-H "TE: ${__headers__[33]}"} \ 385 | ${__headers__[34]:+-H "User-Agent: ${__headers__[34]}"} \ 386 | ${__headers__[35]:+-H "Upgrade: ${__headers__[35]}"} \ 387 | ${__headers__[36]:+-H "Via: ${__headers__[36]}"} \ 388 | ${__headers__[37]:+-H "Warning: ${__headers__[37]}"} \ 389 | --output $__bodyfile__ 2>&1 390 | ) 391 | 392 | # Conteúdo 393 | __ref__[body]=$(< $__bodyfile__) 394 | 395 | return $? 396 | } 397 | 398 | # .FUNCTION http.connection() -> [bool] 399 | # 400 | # Define as configurações de conexão para o objeto apontado por 'conn'. 401 | # 402 | function http.connection() 403 | { 404 | getopt.parse 6 "conn:http_request_t:$1" "method:str:$2" "url:str:$3" "fields:map:$4" "headers:http_header_st:$5" "response:map:$6" "${@:7}" 405 | 406 | printf -v $1 '%s|%s|%s|%s|%s' "$2" "$3" "$4" "$5" "$6" 407 | return $? 408 | } 409 | 410 | # .FUNCTION http.conn_request -> [bool] 411 | # 412 | # Executa a conexão de requisição do objeto. 413 | # 414 | function http.conn_request() 415 | { 416 | getopt.parse 1 "conn:http_request_t:$1" "${@:2}" 417 | 418 | local __opts__ 419 | 420 | IFS='|' read -a __opts__ <<< "${!1}" 421 | http.request "${__opts__[@]}" 422 | 423 | return $? 424 | } 425 | 426 | # .FUNCTION http.connection_data -> [bool] 427 | # 428 | # Como 'http.connection', porém para estrutura de dados. 429 | # 430 | function http.connection_data() 431 | { 432 | getopt.parse 6 "conn:http_requests_t:$1" "method:str:$2" "url:str:$3" "data:str:$4" "headers:http_header_st:$5" "response:map:$6" "${@:7}" 433 | 434 | printf -v $1 '%s|%s|%s|%s|%s' "$2" "$3" "$5" "$6" "$4" 435 | return $? 436 | } 437 | 438 | # .FUNCTION http.conn_request_data -> [bool] 439 | # 440 | # Executa a conexão de requisição do objeto. 441 | # 442 | function http.conn_request_data() 443 | { 444 | getopt.parse 1 "conn:http_requests_t:$1" "${@:2}" 445 | 446 | local __opts__ 447 | 448 | IFS='|' read -ra __opts__ <<< "${!1}" 449 | 450 | http.request_data "${__opts__[0]}" \ 451 | "${__opts__[1]}" \ 452 | "${__opts__[4]}" \ 453 | "${__opts__[2]}" \ 454 | "${__opts__[3]}" 455 | 456 | return $? 457 | } 458 | 459 | # .FUNCTION http.conn_data -> [bool] 460 | # 461 | # Define a estrutura de dados do objeto. 462 | # 463 | function http.conn_data() 464 | { 465 | getopt.parse 2 "conn:http_requests_t:$1" "data:str:$2" "${@:3}" 466 | 467 | local __opts__ 468 | 469 | IFS='|' read -ra __opts__ <<< "${!1}" 470 | printf -v $1 '%s|%s|%s|%s|%s' "${__opts__[@]:0:4}" "$2" 471 | 472 | return $? 473 | } 474 | 475 | # Estruturas 476 | var http_header_st struct_t 477 | 478 | # .STRUCT http_header_st 479 | # 480 | # Implementa o objeto 'S' com os membros: 481 | # 482 | # S.aim [str] 483 | # S.accept [str] 484 | # S.accept_charset [str] 485 | # S.accept_encoding [str] 486 | # S.accept_language [str] 487 | # S.accept_datetime [str] 488 | # S.accept_control_request_method [str] 489 | # S.accept_control_request_headers [str] 490 | # S.authorization [str] 491 | # S.cache_control [str] 492 | # S.connection [str] 493 | # S.content_length [uint] 494 | # S.content_md5 [str] 495 | # S.content_type [str] 496 | # S.cookie [str] 497 | # S.date [str] 498 | # S.expect [str] 499 | # S.forwarded [str] 500 | # S.from [str] 501 | # S.host [str] 502 | # S.http2_settings [str] 503 | # S.if_match [str] 504 | # S.if_modified_since [str] 505 | # S.if_none_match [str] 506 | # S.if_range [str] 507 | # S.if_unmodified_since [str] 508 | # S.max_forwards [uint] 509 | # S.origin [str] 510 | # S.pragma [str] 511 | # S.proxy_authorization [str] 512 | # S.range [str] 513 | # S.referer [str] 514 | # S.te [str] 515 | # S.user_agent [str] 516 | # S.upgrade [str] 517 | # S.via [str] 518 | # S.warning [str] 519 | # 520 | http_header_st.__add__ aim str \ 521 | accept str \ 522 | accept_charset str \ 523 | accept_encoding str \ 524 | accept_language str \ 525 | accept_datetime str \ 526 | accept_control_request_method str \ 527 | accept_control_request_headers str \ 528 | authorization str \ 529 | cache_control str \ 530 | connection str \ 531 | content_length uint \ 532 | content_md5 str \ 533 | content_type str \ 534 | cookie str \ 535 | date str \ 536 | expect str \ 537 | forwarded str \ 538 | from str \ 539 | host str \ 540 | http2_settings str \ 541 | if_match str \ 542 | if_modified_since str \ 543 | if_none_match str \ 544 | if_range str \ 545 | if_unmodified_since str \ 546 | max_forwards uint \ 547 | origin str \ 548 | pragma str \ 549 | proxy_authorization str \ 550 | range str \ 551 | referer str \ 552 | te str \ 553 | user_agent str \ 554 | upgrade str \ 555 | via str \ 556 | warning str 557 | 558 | # .MAP response 559 | # 560 | # Chaves: 561 | # 562 | # access-control-allow-origin 563 | # access-control-allow-credentials 564 | # access-control-expose-headers 565 | # access-control-max-age 566 | # access-control-allow-methods 567 | # access-control-allow-headers 568 | # accept-patch 569 | # accept-ranges 570 | # age 571 | # allow 572 | # alt-svc 573 | # cache-control 574 | # connection 575 | # content-disposition 576 | # content-encoding 577 | # content-language 578 | # content-length 579 | # content-location 580 | # content-md5 581 | # content-range 582 | # content-type 583 | # date 584 | # delta-base 585 | # etag 586 | # expires 587 | # im 588 | # last-modified 589 | # link 590 | # location 591 | # p3p 592 | # pragma 593 | # proxy-authenticate 594 | # public-key-pins 595 | # retry-after 596 | # server 597 | # set-cookie 598 | # strict-transport-security 599 | # trailer 600 | # transfer-encoding 601 | # tk 602 | # upgrade 603 | # vary 604 | # via 605 | # warning 606 | # www-authenticate 607 | # x-frame-options 608 | # proto 609 | # status 610 | # body 611 | # 612 | 613 | # .TYPE http_request_t 614 | # 615 | # Implementa o objeto 'S' com o método: 616 | # 617 | # S.conn_request 618 | # 619 | typedef http_request_t http.conn_request 620 | 621 | # .TYPE http_requests_t 622 | # 623 | # Implementa o objeto 'S' com o métodos: 624 | # 625 | # S.conn_request_data 626 | # S.conn_data 627 | # 628 | typedef http_requests_t http.conn_request_data \ 629 | http.conn_data 630 | 631 | # Funções (somente-leitura) 632 | readonly -f http.get \ 633 | http.request \ 634 | http.request_data \ 635 | http.connection \ 636 | http.conn_request \ 637 | http.connection_data \ 638 | http.conn_request_data \ 639 | http.conn_data 640 | 641 | # /* __HTTP_SH__ */ 642 | -------------------------------------------------------------------------------- /src/json.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __JSON_SH__ ] && return 0 21 | 22 | readonly __JSON_SH__=1 23 | 24 | source builtin.sh 25 | source setup.sh 26 | 27 | # Dependência. 28 | setup.package 'jq (>= 1.5)' 29 | 30 | # .FUNCTION json.load -> [bool] 31 | # 32 | # Converte o arquivo contendo um documento JSON em uma estrutura 33 | # de dados mapeada e salva no objeto apontado por 'obj'. 34 | # 35 | function json.load() 36 | { 37 | getopt.parse 2 "file:str:$1" "obj:map:$2" "${@:3}" 38 | 39 | if [ ! -f "$1" ]; then 40 | error.error "'$1' não é um arquivo regular" 41 | return $? 42 | elif [ ! -r "$1" ]; then 43 | error.error "'$1' não foi possível ler o arquivo" 44 | return $? 45 | fi 46 | 47 | json.__setmap__ file "$1" $2 48 | 49 | return $? 50 | } 51 | 52 | # .FUNCTION json.loads -> [bool] 53 | # 54 | # Converte a expressão JSON em uma estrutura de dados mapeada 55 | # e salva no objeto apontado por 'obj'. 56 | # 57 | # == EXEMPLO == 58 | # 59 | # #!/bin/bash 60 | # 61 | # source json.sh 62 | # source map.sh 63 | # 64 | # # Inicializa o map. 65 | # declare -A dados=() 66 | # 67 | # # Implementa o tipo map. 68 | # var dados map_t 69 | # 70 | # # Processando/convertendo JSON 71 | # json.loads '{"autor":{"nome":"Juliano","sobrenome":"santos","idade":35,"pseudonimo":"SHAMAN"}}' dados 72 | # 73 | # Listando as chaves do mapa. 74 | # dados.keys 75 | # 76 | # echo --- 77 | # 78 | # # Acessando valores. 79 | # dados.get autor.nome 80 | # dados.get autor.pseudonimo 81 | # dados.get autor.idade 82 | # 83 | # == SAÍDA == 84 | # 85 | # autor.nome 86 | # autor.pseudonimo 87 | # autor.sobrenome 88 | # autor.idade 89 | # --- 90 | # Juliano 91 | # SHAMAN 92 | # 35 93 | # 94 | function json.loads() 95 | { 96 | getopt.parse 2 "expr:str:$1" "obj:map:$2" "${@:3}" 97 | json.__setmap__ expr "$1" $2 98 | return $? 99 | } 100 | 101 | # json.__setmap____ 102 | # 103 | # A função interna processa e converte os dados de um arquivo ou 104 | # expressão JSON para um array associativo especificado (map). 105 | # 106 | function json.__setmap__() 107 | { 108 | local __cjson__ __key__ __val__ 109 | local -n __ref__=$3 110 | 111 | # Converte os objetos json em uma lista mapeada de dados. 112 | __cjson__='path(..| 113 | select(type == "string" or type == "number" or type == "boolean"))| 114 | map(if type == "number" then .|tostring|"["+.+"]" else . end)| 115 | join(".")' 116 | 117 | # Lê a chave atual. 118 | while IFS=$'\n' read __key__; do 119 | __key__=${__key__//.\[/\[} 120 | # Lê os dados. 121 | case $1 in 122 | expr) __val__=$(jq ".$__key__" <<< "$2");; 123 | file) __val__=$(jq ".$__key__" "$2");; 124 | esac 2>/dev/null 125 | __val__=${__val__#\"} 126 | __val__=${__val__%\"} 127 | # Salva no objeto os dados da respectiva chave. 128 | __ref__[$__key__]=$__val__ 129 | done < <( 130 | # Processa a linha de comando para o tipo especificado. 131 | case $1 in 132 | expr) jq -r "$__cjson__" <<< "$2";; 133 | file) jq -r "$__cjson__" "$2";; 134 | esac 2>/dev/null 135 | ) 136 | 137 | # Se o objeto não contém elementos processados. 138 | [ ${#__ref__[@]} -eq 0 ] && error.error 'erro ao processar os dados json' 139 | 140 | return $? 141 | } 142 | 143 | # Tipos/Implementações 144 | typedef json_t json.loads 145 | 146 | # .TYPE json_t 147 | # 148 | # Implementa o objeto 'S' com os métodos: 149 | # 150 | # S.loads 151 | # 152 | 153 | # Funções (somente-leitura) 154 | readonly -f json.load \ 155 | json.loads \ 156 | json.__setmap__ 157 | 158 | # /* __JSON_SH__ */ 159 | -------------------------------------------------------------------------------- /src/log.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | # 20 | 21 | [ -v __LOG_SH__ ] && return 0 22 | 23 | readonly __LOG_SH__=1 24 | 25 | # .FUNCTION log.open ... -> [bool] 26 | # 27 | # Grava no arquivo o log com o formato e argumentos especificados. 28 | # 29 | function log.open() 30 | { 31 | getopt.parse -1 "file:str:$1" "fmt:str:$2" "args:str:$3" ... "${@:4}" 32 | 33 | local fmt 34 | 35 | if [ -d "$1" ]; then 36 | error.fatalf "'%s' é um diretório\n" "$1" 37 | elif [ -e "$1" -a ! -w "$1" ]; then 38 | error.fatalf "'%s' permissão negada\n" "$1" 39 | fi 40 | 41 | printf -v fmt '%s: %(%d/%m/%Y %H:%M:%S)T' "${0##*/}" 42 | printf "%s: $2\n" "$fmt" "${@:3}" >> "${1:-/dev/null}" 2>/dev/null || 43 | error.errorf "'%s' formato inválido\n" "$2" 44 | 45 | return $? 46 | } 47 | 48 | # .FUNCTION log.new -> [bool] 49 | # 50 | # Cria e define as configurações do objeto log. 51 | # 52 | function log.new() 53 | { 54 | getopt.parse 3 "log:log_t:$1" "file:str:$2" "fmt:str:$3" "${@:4}" 55 | 56 | printf -v $1 '%s|%s' "${@:2}" 57 | return $? 58 | } 59 | 60 | # .FUNCTION log.write -> [bool] 61 | # 62 | # Grava as informações no log. 63 | # 64 | function log.write() 65 | { 66 | getopt.parse -1 "log:log_t:$1" "args:str:$2" ... "${@:3}" 67 | 68 | local __opts__ 69 | IFS='|' read -ra __opts__ <<< "${!1}" 70 | log.open "${__opts__[@]}" "${@:2}" 71 | return $? 72 | } 73 | 74 | # .TYPE log_t 75 | # 76 | # Implementa o objeto 'S' com os métodos: 77 | # 78 | # S.new 79 | # S.write 80 | # 81 | typedef log_t log.new \ 82 | log.write 83 | 84 | # Funções (somente-leitura) 85 | readonly -f log.open \ 86 | log.new \ 87 | log.write 88 | # /* __LOG_SH__ */ 89 | -------------------------------------------------------------------------------- /src/map.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __MAP_SH__ ] && return 0 21 | 22 | readonly __MAP_SH__=1 23 | 24 | source builtin.sh 25 | 26 | # .FUNCTION map.clone -> [bool] 27 | # 28 | # Copia as chaves de origem para o map de destino apagando 29 | # todos os dados já existentes. 30 | # 31 | function map.clone() 32 | { 33 | getopt.parse 2 "src:map:$1" "dest:map:$2" "${@:3}" 34 | 35 | local -n __ref1__=$1 __ref2__=$2 36 | local __key__ 37 | 38 | # Limpa o map. 39 | __ref2__=() 40 | 41 | for __key__ in "${!__ref1__[@]}"; do 42 | __ref2__[$__key__]=${__ref1__[$__key__]} 43 | done 44 | 45 | return $? 46 | } 47 | 48 | # .FUNCTION map.copy -> [bool] 49 | # 50 | # Copia as chaves de origem para o map de destino sobrescrevendo 51 | # as chaves já existentes. 52 | # 53 | function map.copy() 54 | { 55 | getopt.parse 2 "src:map:$1" "dest:map:$2" "${@:3}" 56 | 57 | local -n __ref1__=$1 __ref2__=$2 58 | local __key__ 59 | 60 | for __key__ in "${!__ref1__[@]}"; do 61 | __ref2__[$__key__]=${__ref1__[$__key__]} 62 | done 63 | 64 | return $? 65 | } 66 | 67 | # .FUNCTION map.fromkeys ... -> [bool] 68 | # 69 | # Inicializa as chaves do container com o valor especificado. 70 | # > Pode ser especificada mais de uma chave. 71 | # 72 | # == EXEMPLO == 73 | # 74 | # source map.sh 75 | # 76 | # # Declarando o map 77 | # declare -A m1=() 78 | # 79 | # map.fromkeys m1 '10' nome sobrenome idade 80 | # 81 | # # Listando chaves. 82 | # for key in "${!m1[@]}"; do 83 | # echo "m1[$key]=${m1[$key]} 84 | # done 85 | # 86 | # == SAÍDA == 87 | # 88 | # m1[nome]=10 89 | # m1[idade]=10 90 | # m1[sobrenome]=10 91 | # 92 | function map.fromkeys() 93 | { 94 | getopt.parse -1 "obj:map:$1" "value:str:$2" "key:str:$3" ... "${@:3}" 95 | 96 | local -n __ref__=$1 97 | local __key__ 98 | 99 | for __key__ in "${@:3}"; do 100 | __ref__[$__key__]=$2 101 | done 102 | return $? 103 | } 104 | 105 | # .FUNCTION map.get -> [str]|[bool] 106 | # 107 | # Retorna o valor da chave. 108 | # 109 | function map.get() 110 | { 111 | getopt.parse 2 "obj:map:$1" "key:str:$2" "${@:3}" 112 | 113 | local -n __ref__=$1 114 | echo "${__ref__[$2]}" 115 | return $? 116 | } 117 | 118 | # .FUNCTION map.keys -> [str]|[bool] 119 | # 120 | # Retorna todas as chaves do container. 121 | # 122 | function map.keys() 123 | { 124 | getopt.parse 1 "obj:map:$1" "${@:2}" 125 | 126 | local -n __ref__=$1 127 | printf '%s\n' "${!__ref__[@]}" 128 | return $? 129 | } 130 | 131 | # .FUNCTION map.sortkeys -> [str]|[bool] 132 | # 133 | # Retorna todas as chaves em ordem alfabética. 134 | # 135 | function map.sortkeys() 136 | { 137 | getopt.parse 1 "obj:map:$1" "${@:2}" 138 | 139 | local -n __ref__=$1 140 | printf '%s\n' "${!__ref__[@]}" | sort -d 141 | return $? 142 | } 143 | 144 | # .FUNCTION map.items -> [str]|[bool] 145 | # 146 | # Retorna o valores do container. 147 | # 148 | function map.items() 149 | { 150 | getopt.parse 1 "obj:map:$1" "${@:2}" 151 | 152 | local -n __ref__=$1 153 | printf '%s\n' "${__ref__[@]}" 154 | return $? 155 | } 156 | 157 | # .FUNCTION map.list -> [str|str]|[bool] 158 | # 159 | # Retorna uma lista iterável que é representação dos elementos 160 | # no container no seguinte formato: 161 | # 162 | # key|value 163 | # 164 | function map.list() 165 | { 166 | getopt.parse 1 "obj:map:$1" "${@:2}" 167 | 168 | local -n __ref__=$1 169 | local __key__ 170 | 171 | for __key__ in "${!__ref__[@]}"; do 172 | printf '%s|%s\n' "$__key__" "${__ref__[$__key__]}" 173 | done 174 | return $? 175 | } 176 | 177 | # .FUNCTION map.remove -> [bool] 178 | # 179 | # Remove a chave do container. 180 | # 181 | function map.remove() 182 | { 183 | getopt.parse 2 "obj:map:$1" "key:str:$2" "${@:3}" 184 | 185 | unset $1[$2] 186 | return $? 187 | } 188 | 189 | # .FUNCTION map.add -> [bool] 190 | # 191 | # Adiciona uma nova chave ao container. 192 | # 193 | # == EXEMPLO == 194 | # 195 | # source map.sh 196 | # 197 | # # Declarando o tipo map 198 | # declare -A usuario=() 199 | # 200 | # # Atribuindo chave/valor 201 | # map.add usuario 'nome' 'Juliano' 202 | # map.add usuario 'sobrenome' 'Santos' 203 | # map.add usuario 'idade' '35' 204 | # 205 | # echo "${usuario[nome]}" 206 | # echo "${usuario[sobrenome]}" 207 | # echo "${usuario[idade]}" 208 | # 209 | # == SAÍDA == 210 | # 211 | # Juliano 212 | # Santos 213 | # 35 214 | # 215 | function map.add() 216 | { 217 | getopt.parse 3 "obj:map:$1" "key:str:$2" "value:str:$3" "${@:4}" 218 | 219 | local -n __ref__=$1 220 | __ref__[$2]=$3 221 | return $? 222 | } 223 | 224 | # .FUNCTION map.contains -> [bool] 225 | # 226 | # Retorna 'true' se contém a chave especificada, caso contrário 'false'. 227 | # 228 | function map.contains() 229 | { 230 | getopt.parse 2 "obj:map:$1" "key:str:$2" "${@:3}" 231 | 232 | [[ -v $1[$2] ]] 233 | return $? 234 | } 235 | 236 | # .FUNCTION map.pop -> [str]|[bool] 237 | # 238 | # Retorna e remove do container a chave especificada. 239 | # 240 | function map.pop() 241 | { 242 | getopt.parse 2 "obj:map:$1" "key:str:$2" "${@:3}" 243 | 244 | local -n __ref__=$1 245 | echo "${__ref__[$2]}" 246 | unset $1[$2] 247 | return $? 248 | } 249 | 250 | # .TYPE map_t 251 | # 252 | # Implementa o objeto 'S' com os métodos: 253 | # 254 | # S.clone 255 | # S.copy 256 | # S.fromkeys 257 | # S.get 258 | # S.keys 259 | # S.items 260 | # S.list 261 | # S.remove 262 | # S.add 263 | # S.contains 264 | # S.pop 265 | # 266 | typedef map_t \ 267 | map.clone \ 268 | map.copy \ 269 | map.fromkeys \ 270 | map.get \ 271 | map.keys \ 272 | map.sortkeys \ 273 | map.items \ 274 | map.list \ 275 | map.remove \ 276 | map.add \ 277 | map.contains \ 278 | map.pop 279 | 280 | # Funções (somente leitura) 281 | readonly -f map.clone \ 282 | map.copy \ 283 | map.fromkeys \ 284 | map.get \ 285 | map.keys \ 286 | map.sortkeys \ 287 | map.items \ 288 | map.list \ 289 | map.remove \ 290 | map.add \ 291 | map.contains \ 292 | map.pop 293 | 294 | # /* __MAP_SH__ */ 295 | -------------------------------------------------------------------------------- /src/mem.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __MEM_SH__ ] && return 0 21 | 22 | readonly __MEM_SH__=1 23 | 24 | source builtin.sh 25 | 26 | # .FUNCTION mem.stats -> [bool] 27 | # 28 | # Obtém estatisticas de uso da memória do sistema. 29 | # 30 | function mem.stats() 31 | { 32 | getopt.parse 1 "stats:map:$1" "${@:2}" 33 | 34 | local __flag__ __size__ 35 | local -n __ref__=$1 36 | 37 | # Inicializa. 38 | __ref__=() || return 1 39 | 40 | while IFS=':' read __flag__ __size__; do 41 | IFS=' ' read __size__ _ <<< $__size__ 42 | __ref__[${__flag__,,}]=$__size__ 43 | done < /proc/meminfo 44 | 45 | return $? 46 | } 47 | 48 | # .FUNCTION mem.physical -> [bool] 49 | # 50 | # Obtém informações da memória física. 51 | # 52 | function mem.physical() 53 | { 54 | getopt.parse 1 "mem:map:$1" "${@:2}" 55 | 56 | local __flag__ __size__ 57 | local -n __ref__=$1 58 | 59 | __ref__=() || return 1 60 | 61 | while IFS=':' read __flag__ __size__; do 62 | IFS=' ' read __size__ _ <<< $__size__ 63 | __flag__=${__flag__,,} 64 | [[ $__flag__ == @(memtotal|memfree|memavailable|cached) ]] && 65 | __ref__[$__flag__]=$__size__ 66 | done < /proc/meminfo 67 | 68 | return $? 69 | } 70 | 71 | # .FUNCTION mem.swap -> [bool] 72 | # 73 | # Obtém informações da memória virtual. 74 | # 75 | function mem.swap() 76 | { 77 | getopt.parse 1 "swap:map:$1" "${@:2}" 78 | 79 | local __flag__ __size__ 80 | local -n __ref__=$1 81 | 82 | __ref__=() || return 1 83 | 84 | while IFS=':' read __flag__ __size__; do 85 | IFS=' ' read __size__ _ <<< $__size__ 86 | __flag__=${__flag__,,} 87 | [[ $__flag__ == @(swaptotal|swapcached|swapfree) ]] && 88 | __ref__[$__flag__]=$__size__ 89 | done < /proc/meminfo 90 | 91 | return $? 92 | } 93 | 94 | # .MAP stats 95 | # 96 | # Chaves: 97 | # 98 | # directmap4k 99 | # hugepages_total 100 | # vmalloctotal 101 | # anonhugepages 102 | # shmemhugepages 103 | # bounce 104 | # active(file) 105 | # buffers 106 | # inactive(file) 107 | # active 108 | # sunreclaim 109 | # inactive(anon) 110 | # mapped 111 | # swaptotal 112 | # swapcached 113 | # hardwarecorrupted 114 | # commitlimit 115 | # memfree 116 | # slab 117 | # writeback 118 | # nfs_unstable 119 | # inactive 120 | # cached 121 | # hugepagesize 122 | # shmem 123 | # dirty 124 | # hugepages_free 125 | # memavailable 126 | # cmatotal 127 | # kernelstack 128 | # cmafree 129 | # sreclaimable 130 | # unevictable 131 | # shmempmdmapped 132 | # writebacktmp 133 | # memtotal 134 | # hugepages_rsvd 135 | # vmallocused 136 | # directmap2m 137 | # swapfree 138 | # active(anon) 139 | # vmallocchunk 140 | # committed_as 141 | # anonpages 142 | # mlocked 143 | # hugepages_surp 144 | # pagetables 145 | # 146 | 147 | # .MAP meminfo 148 | # 149 | # Chaves: 150 | # 151 | # memfree 152 | # cached 153 | # memavailable 154 | # memtotal 155 | # 156 | 157 | # .MAP swap 158 | # 159 | # Chaves: 160 | # 161 | # swaptotal 162 | # swapcached 163 | # swapfree 164 | # 165 | 166 | # Funções 167 | readonly -f mem.stats \ 168 | mem.physical \ 169 | mem.swap 170 | 171 | # /* __MEM_SH__ */ 172 | -------------------------------------------------------------------------------- /src/net.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __NET_SH__ ] && return 0 21 | 22 | readonly __NET_SH__=1 23 | 24 | source builtin.sh 25 | 26 | readonly -A __NET__=( 27 | [ipv4]='^(([0-9]|[1-9][0-9]|1[0-9]{,2}|2[0-4][0-9]|25[0-5])[.]){3}([0-9]|[1-9][0-9]|1[0-9]{,2}|2[0-4][0-9]|25[0-5])$' 28 | [ipv6]='^(([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:)|fe80:(:[0-9a-fA-F]{0,4}){0,4}%[0-9a-zA-Z]{1,}|::(ffff(:0{1,4}){0,1}:){0,1}((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])|([0-9a-fA-F]{1,4}:){1,4}:((25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9])\.){3,3}(25[0-5]|(2[0-4]|1{0,1}[0-9]){0,1}[0-9]))$' 29 | ) 30 | 31 | # .FUNCTION net.getifaces -> [str] 32 | # 33 | # Retorna as interfaces de rede 34 | # 35 | function net.getifaces() 36 | { 37 | getopt.parse 0 "$@" 38 | 39 | local if; printf '%s\n' /sys/class/net/* | 40 | while IFS=$'\n' read if; do echo ${if##*/}; done 41 | return $? 42 | } 43 | 44 | # .FUNCTION net.getifaddrs -> [bool] 45 | # 46 | # Lẽ as informações da interface. 47 | # 48 | # == EXEMPLO == 49 | # 50 | # source net.sh 51 | # 52 | # # Mapa 53 | # declare -A inet=() 54 | # 55 | # # Lê as informações da interface. 56 | # net.getifaddrs 'wlx001a3f8329f2' inet 57 | # 58 | # # Informações. 59 | # echo "Indice:" ${inet[index]} 60 | # echo "Nome:" ${inet[name]} 61 | # echo "Status:" ${inet[state]} 62 | # echo "Dispositivo:" ${inet[dev]} 63 | # echo "IPv4:" ${inet[addr4]} 64 | # 65 | # == SAÍDA == 66 | # 67 | # Indice: 3 68 | # Nome: wlx001a3f8329f2 69 | # Status: up 70 | # Dispositivo: wlan 71 | # IPv4: 192.168.25.4 72 | # 73 | function net.getifaddrs() 74 | { 75 | getopt.parse 2 "iface:str:$1" "ifa:map:$2" "${@:3}" 76 | 77 | local __iface__ __ifname__ __ifindex__ __stats__ 78 | local __dev__ __inet4__ __inet6__ __flag__ __val__ 79 | local -n __ref__=$2 80 | 81 | net.__check_iface__ "$1" && 82 | __ref__=() || return $? 83 | 84 | 85 | __iface__=/sys/class/net/$1 86 | __stats__=$__iface__/statistics 87 | 88 | while IFS='=' read __flag__ __val__; do 89 | case ${__flag__,,} in 90 | devtype) __dev__=$__val__;; 91 | interface) __ifname__=$__val__;; 92 | ifindex) __ifindex__=$__val__; 93 | esac 94 | done < $__iface__/uevent 95 | 96 | if [ ! -v __ifname__ ]; then 97 | error.error "'$1' não foi possível obter informações da interface" 98 | return $? 99 | fi 100 | 101 | IFS='|' read -a __inet4__ <<< $(net.__get_ifaddr__ 4 $1) 102 | IFS='|' read -a __inet6__ <<< $(net.__get_ifaddr__ 6 $1) 103 | 104 | __ref__[index]=$__ifindex__ 105 | __ref__[name]=$__ifname__ 106 | __ref__[dev]=$__dev__ 107 | __ref__[hwaddr]=$(< $__iface__/address) 108 | __ref__[broadcast]=$(< $__iface__/broadcast) 109 | __ref__[mtu]=$(< $__iface__/mtu) 110 | __ref__[state]=$(< $__iface__/operstate) 111 | 112 | __ref__[addr]=${__inet4__[0]} 113 | __ref__[mask]=${__inet4__[1]} 114 | __ref__[vlan]=${__inet4__[2]} 115 | 116 | __ref__[addr6]=${__inet6__[0]} 117 | __ref__[mask6]=${__inet6__[1]} 118 | __ref__[vlan6]=${__inet6__[2]} 119 | 120 | __ref__[tx_packets]=$(< $__stats__/tx_packets) 121 | __ref__[rx_packets]=$(< $__stats__/rx_packets) 122 | __ref__[tx_bytes]=$(< $__stats__/tx_bytes) 123 | __ref__[rx_bytes]=$(< $__stats__/rx_bytes) 124 | __ref__[tx_dropped]=$(< $__stats__/tx_dropped) 125 | __ref__[rx_dropped]=$(< $__stats__/rx_dropped) 126 | __ref__[tx_errors]=$(< $__stats__/tx_errors) 127 | __ref__[rx_errors]=$(< $__stats__/rx_errors) 128 | 129 | return $? 130 | } 131 | 132 | # .FUNCTION net.getifstats -> [bool] 133 | # 134 | # Lê as estatísticas da interface. 135 | # 136 | function net.getifstats() 137 | { 138 | getopt.parse 2 "iface:str:$1" "ifa:map:$2" "${@:3}" 139 | 140 | local -n __ref__=$2 141 | 142 | net.__check_iface__ "$1" && 143 | __ref__=() || return $? 144 | 145 | local __stats__=/sys/class/net/$1/statistics 146 | 147 | __ref__[tx_packets]=$(< $__stats__/tx_packets) 148 | __ref__[rx_packets]=$(< $__stats__/rx_packets) 149 | __ref__[tx_bytes]=$(< $__stats__/tx_bytes) 150 | __ref__[rx_bytes]=$(< $__stats__/rx_bytes) 151 | __ref__[tx_dropped]=$(< $__stats__/tx_dropped) 152 | __ref__[rx_dropped]=$(< $__stats__/rx_dropped) 153 | __ref__[tx_errors]=$(< $__stats__/tx_errors) 154 | __ref__[rx_errors]=$(< $__stats__/rx_errors) 155 | 156 | return $? 157 | } 158 | 159 | # .FUNCTION net.getifaddr -> [bool] 160 | # 161 | # Obtém o endereço ipv4 da interface. 162 | # 163 | function net.getifaddr() 164 | { 165 | getopt.parse 2 "iface:str:$1" "ifa:map:$2" "${@:3}" 166 | 167 | local __inet__ 168 | local -n __ref__=$2 169 | 170 | net.__check_iface__ "$1" && 171 | __ref__=() || return $? 172 | 173 | IFS='|' read -a __inet__ <<< $(net.__get_ifaddr__ 4 $1) 174 | 175 | __ref__[addr]=${__inet__[0]} 176 | __ref__[mask]=${__inet__[1]} 177 | __ref__[vlan]=${__inet__[2]} 178 | 179 | return $? 180 | } 181 | 182 | # .FUNCTION net.getifaddr6 -> [bool] 183 | # 184 | # Obtém o endereço ipv6 da interface. 185 | # 186 | function net.getifaddr6() 187 | { 188 | getopt.parse 2 "iface:str:$1" "ifa:map:$2" "${@:3}" 189 | 190 | local __inet__ 191 | local -n __ref__=$2 192 | 193 | net.__check_iface__ "$1" && 194 | __ref__=() || return $? 195 | 196 | IFS='|' read -a __inet__ <<< $(net.__get_ifaddr__ 6 $1) 197 | 198 | __ref__[addr6]=${__inet__[0]} 199 | __ref__[mask6]=${__inet__[1]} 200 | __ref__[vlan6]=${__inet__[2]} 201 | 202 | return $? 203 | } 204 | 205 | # .FUNCTION net.gethostbyname -> [str]|[bool] 206 | # 207 | # Retorna uma lista de endereços ipv4 e ipv6 (se disponnível) do endereço especificado. 208 | # 209 | net.gethostbyname() 210 | { 211 | getopt.parse 1 "addr:str:$1" "${@:2}" 212 | 213 | local expr 214 | 215 | if ! (host -t A $1 && host -t AAAA $1); then 216 | error.error "'$1' host não encontrado" 217 | return $? 218 | fi | while IFS=' ' read -a expr; do 219 | [[ ${expr[-1]} != 3\(NXDOMAIN\) ]] && 220 | echo ${expr[-1]} 221 | done 222 | 223 | return $? 224 | } 225 | 226 | # .FUNCTION net.gethostbyaddr -> [str]|[bool] 227 | # 228 | # Retorna uma lista com os nomes mapeados para o endereço especificado. 229 | # 230 | function net.gethostbyaddr() 231 | { 232 | getopt.parse 1 "addr:str:$1" "${@:2}" 233 | 234 | local expr 235 | 236 | if ! host -t PTR $1; then 237 | error.error "'$1' host não encontrado" 238 | return $? 239 | fi | while IFS=' ' read -a expr; do 240 | [[ ${expr[-1]} != 3\(NXDOMAIN\) ]] && 241 | echo ${expr[-1]} 242 | done 243 | 244 | return $? 245 | } 246 | 247 | # .FUNCTION net.nslookup -> [bool] 248 | # 249 | # Resolve os registros DNS. 250 | # 251 | function net.nslookup() 252 | { 253 | getopt.parse 2 "addr:str:$1" "dnsreg:map:$2" "${@:3}" 254 | 255 | local __expr__ __ok__ __i__ 256 | local -n __ref__=$2 257 | 258 | __ref__=() || return 1 259 | 260 | while IFS=$'\t' read -a __expr__; do 261 | if [[ ${__expr__[3]} == @(A|AAAA|NS|CNAME|MX|PTR|TXT|SOA|SPF|SRV) ]]; then 262 | __ref__[name[${__i__:=0}]]=${__expr__[0]} 263 | __ref__[ttl[$__i__]]=${__expr__[1]} 264 | __ref__[class[$__i__]]=${__expr__[2]} 265 | __ref__[type[$__i__]]=${__expr__[3]} 266 | __ref__[addr[$__i__]]=${__expr__[4]} 267 | ((__i__++)) 268 | __ok__=true 269 | fi 270 | done < <(host -a $1) 271 | 272 | [[ $__ok__ ]] || error.error "'$1' endereço não encontrado" 273 | 274 | return $? 275 | } 276 | 277 | # .FUNCTION net.ping -> [bool] 278 | # 279 | # Envia ICMP_HOST_REQUEST para o endereço de rede. 280 | # 281 | # == EXEMPLO == 282 | # 283 | # source net.sh 284 | # 285 | # # Mapa 286 | # declare -A stat=() 287 | # 288 | # # Envia 4 pacotes. 289 | # net.ping 'google.com' 4 stat 290 | # 291 | # # Imprime as estatísticas. 292 | # for key in ${!stat[@]}; do 293 | # echo "$key = ${stat[$key]}" 294 | # done 295 | # 296 | # == SAÍDA == 297 | # 298 | # host = rio01s20-in-f46.1e100.net 299 | # addr = 172.217.29.46 300 | # tx_packets = 4 301 | # rx_packets = 4 302 | # lp_packets = 0 303 | # time = 3005 304 | # ttl = 56 305 | # rtt_min = 4.664 306 | # rtt_max = 39.301 307 | # rtt_avg = 22.536 308 | # rtt_mdev = 16.211 309 | # 310 | function net.ping() 311 | { 312 | getopt.parse 3 "addr:str:$1" "count:uint:$2" "stats:map:$3" "${@:4}" 313 | 314 | local __expr__ __ipv4__ __ipv6__ __stats__ 315 | local __i__ __ttl__ __addr__ __host__ __cping__ 316 | local -n __ref__=$3 317 | 318 | __ref__=() || return 1 319 | 320 | # Define a versão do comando ping. 321 | [[ $1 =~ ${__NET__[ipv6]} ]] && __cping__=ping6 || __cping__=ping 322 | 323 | __ipv4__=${__NET__[ipv4]} 324 | __ipv6__=${__NET__[ipv6]} 325 | 326 | # Remove ancoras 327 | __ipv4__=${__ipv4__#?}; __ipv4__=${__ipv4__%?} 328 | __ipv6__=${__ipv6__#?}; __ipv6__=${__ipv6__%?} 329 | 330 | # Extrai as informações da saída padrão. 331 | # addr, host, ttl, stats e rtt 332 | while IFS=$'\n' read __expr__; do 333 | if [[ ! $__addr__ && $__expr__ =~ \(($__ipv4__|$__ipv6__)\) ]]; then 334 | __addr__=${BASH_REMATCH[1]} 335 | elif [[ ! $__host__ && $__expr__ =~ from\ +([^ ]+).+\ +ttl=([0-9]+) ]]; then 336 | __host__=${BASH_REMATCH[1]} 337 | __ttl__=${BASH_REMATCH[2]} 338 | elif [[ $__expr__ =~ (packets|rtt) ]]; then 339 | while [[ $__expr__ =~ [0-9]+(\.[0-9]+)? ]]; do 340 | __stats__[$((__i__++))]=$BASH_REMATCH 341 | __expr__=${__expr__/$BASH_REMATCH} 342 | done 343 | fi 344 | done < <($__cping__ -c$2 -W5 $1 2>/dev/null) 345 | 346 | # Sem estatíticas. 347 | if [ ! -v __stats__ ]; then 348 | error.error "'$1' host não encontrado" 349 | return $? 350 | fi 351 | 352 | 353 | # Valores 354 | __ref__[host]=$__host__ 355 | __ref__[addr]=$__addr__ 356 | __ref__[ttl]=$__ttl__ 357 | __ref__[tx_packets]=${__stats__[0]} 358 | __ref__[rx_packets]=${__stats__[1]} 359 | __ref__[lp_packets]=${__stats__[2]} 360 | __ref__[time]=${__stats__[3]} 361 | __ref__[rtt_min]=${__stats__[4]} 362 | __ref__[rtt_avg]=${__stats__[5]} 363 | __ref__[rtt_max]=${__stats__[6]} 364 | __ref__[rtt_mdev]=${__stats__[7]} 365 | 366 | return $? 367 | } 368 | 369 | # .FUNCTION net.isaddr -> [bool] 370 | # 371 | # Retorna 'true' se o endereço é do tipo ipv4. 372 | # 373 | function net.isaddr() 374 | { 375 | getopt.parse 1 "addr:str:$1" "${@:2}" 376 | [[ $1 =~ ${__NET__[ipv4]} ]] 377 | return $? 378 | } 379 | 380 | # .FUNCTION net.isaddr6 -> [bool] 381 | # 382 | # Retorna 'true' se o endereço é do tipo ipv6. 383 | # 384 | function net.isaddr6() 385 | { 386 | getopt.parse 1 "addr:str:$1" "${@:2}" 387 | [[ $1 =~ ${__NET__[ipv6]} ]] 388 | return $? 389 | } 390 | 391 | # .FUNCTION net.getexternalip => [str]|[bool] 392 | # 393 | # Obtem o endereço de ip externo. 394 | # 395 | function net.getexternalip() 396 | { 397 | getopt.parse 0 "$@" 398 | 399 | local ok url urls 400 | 401 | local urls=('api.ipify.org' 402 | 'ip.42.pl/raw' 403 | 'myexternalip.com/raw' 404 | 'ipecho.net/plain' 405 | 'icanhazip.com') 406 | 407 | for url in ${urls[@]}; do 408 | wget -T3 -qO- "$url" 2>/dev/null && 409 | echo && break 410 | done 411 | 412 | (($?)) && error.error 'não foi possível obter o endereço externo' 413 | 414 | return $? 415 | } 416 | 417 | function net.__get_ifaddr__() 418 | { 419 | local ip mask vlan bsize bs bu bits inet 420 | 421 | bsize=$(($1 == 4 ? 32 : $(($1 == 6 ? 128 : 0)))) 422 | 423 | ((bsize == 0)) && return 1 424 | 425 | inet='inet6?\s+([^/]+)/([0-9]+)' 426 | 427 | if [[ $(ip -$1 -o addr show dev $2) =~ $inet ]]; then 428 | 429 | ip=${BASH_REMATCH[1]} 430 | vlan=${BASH_REMATCH[2]} 431 | 432 | printf -v bs '%*s' $vlan 433 | printf -v bu '%*s' $((bsize-vlan)) 434 | 435 | bits=${bs// /1}${bu// /0} 436 | 437 | case $1 in 438 | 4) 439 | printf -v mask '%d.%d.%d.%d' $((2#${bits:0:8})) \ 440 | $((2#${bits:8:8})) \ 441 | $((2#${bits:16:8})) \ 442 | $((2#${bits:24:8})) 443 | ;; 444 | 6) 445 | printf -v mask '%x:%x:%x:%x:%x:%x:%x:%x' $((2#${bits:0:16})) \ 446 | $((2#${bits:16:16})) \ 447 | $((2#${bits:32:16})) \ 448 | $((2#${bits:48:16})) \ 449 | $((2#${bits:64:16})) \ 450 | $((2#${bits:80:16})) \ 451 | $((2#${bits:96:16})) \ 452 | $((2#${bits:112:16})) 453 | ;; 454 | esac 455 | fi 456 | 457 | echo "${ip:-0.0.0.0}|${mask:-0.0.0.0}|${vlan:-0}" 458 | 459 | return 0 460 | } 461 | 462 | function net.__check_iface__() 463 | { 464 | [ -L /sys/class/net/$1 ] || error.error "'$1' interface não encontrada" 465 | return $? 466 | } 467 | 468 | # .MAP ifa 469 | # 470 | # Chaves: 471 | # 472 | # index 473 | # name 474 | # dev 475 | # hwaddr 476 | # broadcast 477 | # mtu 478 | # state 479 | # addr 480 | # mask 481 | # vlan 482 | # addr6 483 | # mask6 484 | # vlan6 485 | # tx_packets 486 | # rx_packets 487 | # tx_bytes 488 | # rx_bytes 489 | # tx_dropped 490 | # rx_dropped 491 | # tx_errors 492 | # rx_errors 493 | # 494 | 495 | # .MAP dnsreg 496 | # 497 | # Chaves: 498 | # 499 | # name[N] 500 | # ttl[N] 501 | # class[N] 502 | # type[N] 503 | # addr[N] 504 | # 505 | # 'N' é o índice do registro. 506 | # 507 | 508 | # .MAP stats 509 | # 510 | # Chaves: 511 | # 512 | # host 513 | # addr 514 | # ttl 515 | # tx_packets 516 | # rx_packets 517 | # lp_packets 518 | # time 519 | # rtt_min 520 | # rtt_avg 521 | # rtt_max 522 | # rtt_mdev 523 | # 524 | 525 | # .TYPE iface_t 526 | # 527 | # Implementa o objeto 'S' com os métodos: 528 | # 529 | # S.getifaddrs 530 | # S.getifstats 531 | # S.getifaddr 532 | # S.getifaddr6 533 | # 534 | typedef iface_t net.getifaddrs \ 535 | net.getifstats \ 536 | net.getifaddr \ 537 | net.getifaddr6 538 | 539 | # .TYPE addr_t 540 | # 541 | # Implementa o objeto 'S' com os métodos: 542 | # 543 | # S.gethostbyname 544 | # S.gethostbyaddr 545 | # S.nslookup 546 | # S.ping 547 | # S.isaddr 548 | # S.isaddr6 549 | # 550 | typedef addr_t net.gethostbyname \ 551 | net.gethostbyaddr \ 552 | net.nslookup \ 553 | net.ping \ 554 | net.isaddr \ 555 | net.isaddr6 556 | 557 | # Funções (somente leitura) 558 | readonly -f net.getifaces \ 559 | net.getifaddrs \ 560 | net.getifstats \ 561 | net.getifaddr \ 562 | net.getifaddr6 \ 563 | net.gethostbyaddr \ 564 | net.nslookup \ 565 | net.ping \ 566 | net.isaddr \ 567 | net.isaddr6 \ 568 | net.getexternalip \ 569 | net.__get_ifaddr__ \ 570 | net.__check_iface__ 571 | 572 | # /* __NET_SH__ */ 573 | 574 | -------------------------------------------------------------------------------- /src/os.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __OS_SH__ ] && return 0 21 | 22 | readonly __OS_SH__=1 23 | 24 | source builtin.sh 25 | 26 | # .FUNCTION os.uname -> [bool] 27 | # 28 | # Obtém informações sobre o kernel atual. 29 | # 30 | function os.uname() 31 | { 32 | getopt.parse 1 "uts:map:$1" "${@:2}" 33 | 34 | local __kernel__=/proc/sys/kernel 35 | local -n __ref__=$1 36 | 37 | __ref__=() || return 1 38 | 39 | __ref__[sysname]=$(< $__kernel__/ostype) 40 | __ref__[nodename]=$(< $__kernel__/hostname) 41 | __ref__[release]=$(< $__kernel__/osrelease) 42 | __ref__[version]=$(< $__kernel__/version) 43 | __ref__[domainname]=$(< $__kernel__/domainname) 44 | __ref__[machine]=$(arch) 45 | 46 | return $? 47 | } 48 | 49 | # .MAP uts 50 | # 51 | # Chaves: 52 | # 53 | # sysname 54 | # nodename 55 | # release 56 | # version 57 | # domainname 58 | # machine 59 | # 60 | 61 | # Função (somente leitura) 62 | readonly -f os.uname 63 | 64 | # /* __OS_SH__ */ 65 | -------------------------------------------------------------------------------- /src/path.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __PATH_SH__ ] && return 0 21 | 22 | readonly __PATH_SH__=1 23 | 24 | source builtin.sh 25 | 26 | # .FUNCTION path.stat -> [bool] 27 | # 28 | # Obtém informações do arquivo. 29 | # 30 | function path.stat() 31 | { 32 | getopt.parse 2 "pathname:str:$1" "stat:map:$2" "${@:3}" 33 | 34 | local __stat__ 35 | local -n __ref__=$2 36 | 37 | __ref__=() || return 1 38 | 39 | [[ ! -r "$1" ]] && { error.error "'$1' não foi possível ler o arquivo"; return $?; } 40 | 41 | IFS='|' read -a __stat__ < <(stat -c '%d|%i|%a|%h|%u|%g|%s|%X|%Y|%Z' "$1") 42 | 43 | __ref__[st_dev]=${__stat__[0]} 44 | __ref__[st_ino]=${__stat__[1]} 45 | __ref__[st_mode]=${__stat__[2]} 46 | __ref__[st_nlink]=${__stat__[3]} 47 | __ref__[st_uid]=${__stat__[4]} 48 | __ref__[st_gid]=${__stat__[5]} 49 | __ref__[st_size]=${__stat__[6]} 50 | __ref__[st_atime]=${__stat__[7]} 51 | __ref__[st_mtime]=${__stat__[8]} 52 | __ref__[st_ctime]=${__stat__[9]} 53 | 54 | return $? 55 | } 56 | 57 | # .FUNCTION path.glob -> [str]|[bool] 58 | # 59 | function path.glob() 60 | { 61 | getopt.parse 1 "path:str:$1" "${@:2}" 62 | 63 | local path 64 | 65 | IFS=$'\t' read path _ < <(path.split "$1") 66 | printf '%s\n' "$path"${1##*/} 67 | 68 | return $? 69 | } 70 | 71 | # .FUNCTION path.scandir -> [str]|[bool] 72 | # 73 | # Retorna os arquivos contidos no caminho. 74 | # 75 | function path.scandir() 76 | { 77 | getopt.parse 1 "path:str:$1" "${@:2}" 78 | 79 | local path 80 | 81 | if [[ ! -d "$1" ]]; then 82 | error.error "'$1' não é um diretório" 83 | return $? 84 | elif [[ ! -x "$1" ]]; then 85 | error.error "'$1' permissão negada" 86 | return $? 87 | fi 88 | 89 | printf '%s\n' "${1%/}/".* "${1%/}/"* 90 | 91 | return $? 92 | } 93 | 94 | # .FUNCTION path.walk -> [str]|[bool] 95 | # 96 | # Retorna os arquivos contidos no diretório e sub-diretórios. 97 | # 98 | function path.walk() 99 | { 100 | getopt.parse 1 "path:str:$1" "${@:2}" 101 | 102 | local path 103 | 104 | if [[ ! -d "$1" ]]; then 105 | error.error "'$1' não é um diretório" 106 | return $? 107 | elif [[ ! -x "$1" ]]; then 108 | error.error "'$1' permissão negada" 109 | return $? 110 | fi 111 | 112 | for path in "${1%/}/"* "${1%/}/".*; do 113 | path=${path%\/\*} 114 | [[ $1 == $path || ${path##*/} == @(.|..) ]] && continue 115 | echo "$path" 116 | [[ -d $path ]] && path.walk "$path" 117 | done 118 | 119 | return $? 120 | } 121 | 122 | # .FUNCTION path.fnwalk -> [str]|[bool] 123 | # 124 | # Escaneia os arquivos contidos no diretório e sub-diretórios, aplicando a 125 | # função filtro a cada iteração e retorna o caminho do arquivo somente se 126 | # o retorno da função for 'true'. 127 | # 128 | # == EXEMPLO == 129 | # 130 | # source path.sh 131 | # 132 | # # Somente arquivos com extensão: '.conf' e '.desktop' 133 | # filtro() 134 | # { 135 | # [[ $1 =~ \.(conf|desktop)$ ]] 136 | # return $? 137 | # } 138 | # 139 | # path.fnwalk '/etc' filtro 140 | # 141 | # == SAÍDA == 142 | # 143 | # /etc/udev/udev.conf 144 | # /etc/ufw/sysctl.conf 145 | # /etc/ufw/ufw.conf 146 | # /etc/xdg/autostart/gnome-keyring-pkcs11.desktop 147 | # /etc/xdg/autostart/gnome-keyring-secrets.desktop 148 | # /etc/xdg/autostart/gnome-keyring-ssh.desktop 149 | # /etc/xdg/autostart/gnome-screensaver.desktop 150 | # ... 151 | # 152 | function path.fnwalk() 153 | { 154 | getopt.parse 2 "path:str:$1" "func:function:$2" "${@:3}" 155 | 156 | local path 157 | 158 | if [[ ! -d "$1" ]]; then 159 | error.error "'$1' não é um diretório" 160 | return $? 161 | elif [[ ! -x "$1" ]]; then 162 | error.error "'$1' permissão negada" 163 | return $? 164 | fi 165 | 166 | for path in "${1%/}/"* "${1%/}/".*; do 167 | path=${path%\/\*} 168 | [[ $1 == $path || ${path##*/} == @(.|..) ]] && continue 169 | "$2" "$path" && echo "$path" 170 | [[ -d $path ]] && path.fnwalk "$path" "$2" 171 | done 172 | 173 | return $? 174 | } 175 | 176 | # .FUNCTION path.ext -> [str]|[bool] 177 | # 178 | # Retorna a extensão do arquivo. 179 | # 180 | function path.ext() 181 | { 182 | getopt.parse 1 "path:str:$1" "${@:2}" 183 | 184 | local ext 185 | IFS='.' read _ ext <<< ${1##*/} 186 | echo "${ext:+.$ext}" 187 | 188 | return $? 189 | } 190 | 191 | # .FUNCTION path.split -> [str]|[bool] 192 | # 193 | # Divide o caminho imediatamente após o separador final, 194 | # separando-o em um diretório e um componente de nome de arquivo. 195 | # 196 | function path.split() 197 | { 198 | getopt.parse 1 "path:str:$1" "${@:2}" 199 | 200 | local split path file 201 | 202 | IFS='/' read -a split <<< "$1" 203 | 204 | case ${#split[@]} in 205 | 0);; 206 | 1) file=$1;; 207 | *) path=${1%/*}; file=${1##*/} 208 | esac 209 | 210 | printf '%s\n%s\n' "$path" "$file" 211 | 212 | return $? 213 | } 214 | 215 | # .FUNCTION path.basename -> [str]|[bool] 216 | # 217 | # Retorna o componente final de um nome de caminho. 218 | # 219 | function path.basename() 220 | { 221 | getopt.parse 1 "path:str:$1" "${@:2}" 222 | 223 | echo "${1##*/}" 224 | return $? 225 | } 226 | 227 | # .FUNCTION path.dirname -> [str]|[bool] 228 | # 229 | # Retorna o componente de diretório de um nome de caminho. 230 | # 231 | function path.dirname() 232 | { 233 | local dir 234 | IFS=$'\t' read dir _ < <(path.split "$1") 235 | echo "$dir" 236 | return $? 237 | } 238 | 239 | # .FUNCTION path.join -> [str]|[bool] 240 | # 241 | # Junte dois ou mais componentes de nome de caminho, 242 | # inserindo '/' conforme necessário. 243 | # 244 | function path.join() 245 | { 246 | getopt.parse -1 "elem:str:$1" ... "${@:2}" 247 | 248 | printf '%s/' "$@"; echo 249 | return $? 250 | } 251 | 252 | # .MAP stat 253 | # 254 | # Chaves: 255 | # 256 | # st_dev 257 | # st_ino 258 | # st_mode 259 | # st_nlink 260 | # st_uid 261 | # st_gid 262 | # st_size 263 | # st_atime 264 | # st_mtime 265 | # st_ctime 266 | # 267 | 268 | # .TYPE path_t 269 | # 270 | # Implementa o objeto 'S" com os métodos: 271 | # 272 | # S.stat 273 | # S.glob 274 | # S.scandir 275 | # S.walk 276 | # S.fnwalk 277 | # S.ext 278 | # S.split 279 | # S.basename 280 | # 281 | typedef path_t path.stat \ 282 | path.glob \ 283 | path.scandir \ 284 | path.walk \ 285 | path.fnwalk \ 286 | path.ext \ 287 | path.split \ 288 | path.basename \ 289 | path.dirname 290 | 291 | # Função (somente leitura) 292 | readonly -f path.stat \ 293 | path.glob \ 294 | path.scandir \ 295 | path.walk \ 296 | path.fnwalk \ 297 | path.ext \ 298 | path.split \ 299 | path.basename \ 300 | path.join \ 301 | path.dirname 302 | 303 | # /* __PATH_SH__ */ 304 | -------------------------------------------------------------------------------- /src/ps.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __PS_SH__ ] && return 0 21 | 22 | readonly __PS_SH__=1 23 | 24 | source builtin.sh 25 | 26 | # .FUNCTION ps.pids -> [uint]|[bool] 27 | # 28 | # Retorna uma lista iterável com o pid dos processos em execução. 29 | # 30 | function ps.pids() 31 | { 32 | getopt.parse 0 "$@" 33 | 34 | local pid 35 | 36 | for pid in /proc/*; do 37 | pid=${pid##*/} 38 | [[ $pid == +([0-9]) ]] && 39 | echo $pid 40 | done | sort -n 41 | 42 | return $? 43 | } 44 | 45 | # .FUNCTION ps.pidof -> [uint]|[bool] 46 | # 47 | # Retorna o ID do processo do programa em execução. 48 | # 49 | function ps.pidof() 50 | { 51 | getopt.parse 1 "procname:str:$1" "${@:2}" 52 | 53 | local pid exe 54 | 55 | for pid in $(ps.pids); do 56 | exe=$(readlink /proc/$pid/exe) 57 | [ "$1" == "${exe##*/}" ] && 58 | echo $pid && 59 | break 60 | done 61 | 62 | return $? 63 | } 64 | 65 | # .FUNCTION ps.stats -> [bool] 66 | # 67 | # Obtém estatísticas do ID do processo. 68 | # 69 | # == EXEMPLO == 70 | # 71 | # #!/bin/bash 72 | # 73 | # source ps.sh 74 | # 75 | # # Inicializa o mapa. 76 | # declare -A stat=() 77 | # 78 | # var pid pspid_t # Implementa os métodos do tipo 'pspid_t' 79 | # 80 | # # Obtém o ID do processo. 81 | # pid=$(ps.pidof 'Telegram') 82 | # 83 | # # Salva em 'stat' as estatísticas do processo. 84 | # pid.stats stat 85 | # 86 | # # Informações. 87 | # echo "Processo:" ${stat[comm]} 88 | # echo "PID:" ${stat[pid]} 89 | # echo "Threads:" ${stat[num_threads]} 90 | # echo "Prioridade:" ${stat[priority]} 91 | # 92 | # == SAÍDA == 93 | # 94 | # Processo: Telegram 95 | # PID: 2545 96 | # Threads: 12 97 | # Prioridade: 20 98 | # 99 | function ps.stats() 100 | { 101 | getopt.parse 2 "pid:uint:$1" "stats:map:$2" "${@:3}" 102 | 103 | local __exe__ __stat__ 104 | local -n __ref__=$2 105 | 106 | ps.__cpid__ $1 && 107 | __ref__=() || return 1 108 | 109 | __exe__=$(readlink /proc/$1/exe) 110 | IFS=' ' read -a __stat__ < /proc/$1/stat 111 | 112 | __ref__[pid]=$1 113 | __ref__[comm]=${__exe__##*/} 114 | __ref__[state]=${__stat__[-50]} 115 | __ref__[ppid]=${__stat__[-49]} 116 | __ref__[pgrp]=${__stat__[-48]} 117 | __ref__[session]=${__stat__[-47]} 118 | __ref__[tty_nr]=${__stat__[-46]} 119 | __ref__[tpgid]=${__stat__[-45]} 120 | __ref__[flags]=${__stat__[-44]} 121 | __ref__[minflt]=${__stat__[-43]} 122 | __ref__[cminflt]=${__stat__[-42]} 123 | __ref__[majflt]=${__stat__[-41]} 124 | __ref__[cmajflt]=${__stat__[-40]} 125 | __ref__[utime]=${__stat__[-39]} 126 | __ref__[stime]=${__stat__[-38]} 127 | __ref__[cutime]=${__stat__[-37]} 128 | __ref__[cstime]=${__stat__[-36]} 129 | __ref__[priority]=${__stat__[-35]} 130 | __ref__[nice]=${__stat__[-34]} 131 | __ref__[num_threads]=${__stat__[-33]} 132 | __ref__[itrealvalue]=${__stat__[-32]} 133 | __ref__[starttime]=${__stat__[-31]} 134 | __ref__[vsize]=${__stat__[-30]} 135 | __ref__[rss]=${__stat__[-29]} 136 | __ref__[rsslim]=${__stat__[-28]} 137 | __ref__[startcode]=${__stat__[-27]} 138 | __ref__[endcode]=${__stat__[-26]} 139 | __ref__[startstack]=${__stat__[-25]} 140 | __ref__[kstkesp]=${__stat__[-24]} 141 | __ref__[kstkeip]=${__stat__[-23]} 142 | __ref__[signal]=${__stat__[-22]} 143 | __ref__[blocked]=${__stat__[-21]} 144 | __ref__[sigignore]=${__stat__[-20]} 145 | __ref__[sigcatch]=${__stat__[-19]} 146 | __ref__[wchan]=${__stat__[-18]} 147 | __ref__[nswap]=${__stat__[-17]} 148 | __ref__[cnswap]=${__stat__[-16]} 149 | __ref__[exit_signal]=${__stat__[-15]} 150 | __ref__[processor]=${__stat__[-14]} 151 | __ref__[rt_priority]=${__stat__[-13]} 152 | __ref__[policy]=${__stat__[-12]} 153 | __ref__[delayacct_blkio_ticks]=${__stat__[-11]} 154 | __ref__[guest_time]=${__stat__[-10]} 155 | __ref__[cguest_time]=${__stat__[-9]} 156 | __ref__[start_data]=${__stat__[-8]} 157 | __ref__[end_data]=${__stat__[-7]} 158 | __ref__[start_brk]=${__stat__[-6]} 159 | __ref__[arg_start]=${__stat__[-5]} 160 | __ref__[arg_end]=${__stat__[-4]} 161 | __ref__[env_start]=${__stat__[-3]} 162 | __ref__[env_end]=${__stat__[-2]} 163 | __ref__[exit_code]=${__stat__[-1]} 164 | 165 | return $? 166 | } 167 | 168 | # .FUNCTION ps.mem -> [bool] 169 | # 170 | # Obtém informações sobre o uso da memória pelo ID do processo. 171 | # 172 | function ps.mem() 173 | { 174 | getopt.parse 2 "pid:uint:$1" "meminfo:map:$2" "${@:3}" 175 | 176 | local __size__ 177 | local -n __ref__=$2 178 | 179 | ps.__cpid__ $1 && 180 | __ref__=() || return 1 181 | 182 | IFS=' ' read -a __size__ < /proc/$1/statm 183 | 184 | __ref__[size]=${__size__[0]} 185 | __ref__[resident]=${__size__[1]} 186 | __ref__[share]=${__size__[2]} 187 | __ref__[text]=${__size__[3]} 188 | __ref__[lib]=${__size__[4]} 189 | __ref__[data]=${__size__[5]} 190 | __ref__[dt]=${__size__[6]} 191 | 192 | return $? 193 | } 194 | 195 | # .FUNCTION ps.io -> [bool] 196 | # 197 | # Obtém estatísticas I/O do ID do processo. 198 | # 199 | function ps.io() 200 | { 201 | getopt.parse 2 "pid:uint:$1" "io:map:$2" "${@:3}" 202 | 203 | local __flag__ __size__ 204 | local -n __ref__=$2 205 | 206 | ps.__cpid__ $1 && 207 | __ref__=() || return 1 208 | 209 | while IFS=':' read __flag__ __size__; do 210 | __ref__[${__flag__,,}]=$__size__ 211 | done < /proc/$1/io 212 | 213 | return $? 214 | } 215 | 216 | # .FUNCTION ps.info -> [bool] 217 | # 218 | # Lê as informações associadas ao ID do processo. 219 | # 220 | # Inicializa o mapa 'S' com as chaves: 221 | # 222 | # S[tty] 223 | # S[time] 224 | # S[user] 225 | # S[start] 226 | # S[vsz] 227 | # S[mem] 228 | # S[pid] 229 | # S[rss] 230 | # S[state] 231 | # S[cmd] 232 | # S[cpu] 233 | # 234 | function ps.info() 235 | { 236 | getopt.parse 2 "pid:uint:$1" "info:map:$2" "${@:3}" 237 | 238 | local __info__ 239 | local -n __ref__=$2 240 | 241 | ps.__cpid__ $1 && 242 | __ref__=() || return 1 243 | 244 | mapfile __info__ < <(ps -q $1 -o user,pid,%cpu,%mem,vsz,rss,tty,state,start,time,cmd) 245 | IFS=' ' read -a __info__ <<< ${__info__[-1]} 246 | 247 | __ref__[user]=${__info__[0]} 248 | __ref__[pid]=${__info__[1]} 249 | __ref__[cpu]=${__info__[2]} 250 | __ref__[mem]=${__info__[3]} 251 | __ref__[vsz]=${__info__[4]} 252 | __ref__[rss]=${__info__[5]} 253 | __ref__[tty]=${__info__[6]} 254 | __ref__[state]=${__info__[7]} 255 | __ref__[start]=${__info__[8]} 256 | __ref__[time]=${__info__[9]} 257 | __ref__[cmd]=${__info__[*]:10} 258 | 259 | return $? 260 | } 261 | 262 | function ps.__cpid__() 263 | { 264 | [ -e /proc/$1 ] || error.error "'$1' pid do processo não encontrado" 265 | return $? 266 | } 267 | 268 | # .MAP stats 269 | # 270 | # Chaves: 271 | # 272 | # flags 273 | # start_brk 274 | # wchan 275 | # guest_time 276 | # processor 277 | # sigcatch 278 | # cutime 279 | # priority 280 | # cnswap 281 | # exit_signal 282 | # nice 283 | # tty_nr 284 | # kstkeip 285 | # cstime 286 | # cminflt 287 | # nswap 288 | # ppid 289 | # comm 290 | # sigignore 291 | # pgrp 292 | # majflt 293 | # blocked 294 | # arg_start 295 | # signal 296 | # endcode 297 | # itrealvalue 298 | # kstkesp 299 | # pid 300 | # stime 301 | # startcode 302 | # env_start 303 | # session 304 | # vsize 305 | # cmajflt 306 | # arg_end 307 | # utime 308 | # startstack 309 | # rss 310 | # policy 311 | # rsslim 312 | # delayacct_blkio_ticks 313 | # starttime 314 | # rt_priority 315 | # minflt 316 | # start_data 317 | # cguest_time 318 | # exit_code 319 | # tpgid 320 | # env_end 321 | # state 322 | # end_data 323 | # num_threads 324 | # 325 | 326 | # .MAP meminfo 327 | # 328 | # Chaves: 329 | # 330 | # size 331 | # resident 332 | # share 333 | # text 334 | # lib 335 | # data 336 | # dt 337 | # 338 | 339 | # .MAP io 340 | # 341 | # Chaves: 342 | # 343 | # cancelled_write_bytes 344 | # wchar 345 | # read_bytes 346 | # write_bytes 347 | # syscw 348 | # syscr 349 | # rchar 350 | # 351 | 352 | # .TYPE pspid_t 353 | # 354 | # Implementa o objeto 'S' com os métodos: 355 | # 356 | # S.stats 357 | # S.mem 358 | # S.io 359 | # S.info 360 | # 361 | typedef pspid_t ps.stats \ 362 | ps.mem \ 363 | ps.io \ 364 | ps.info 365 | 366 | # Funções (somente-leitura) 367 | readonly -f ps.pids \ 368 | ps.pidof \ 369 | ps.stats \ 370 | ps.mem \ 371 | ps.io \ 372 | ps.info \ 373 | ps.__cpid__ 374 | 375 | # /* __PS_SH__ */ 376 | -------------------------------------------------------------------------------- /src/rand.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __RAND_SH__ ] && return 0 21 | 22 | readonly __RAND_SH__=1 23 | 24 | source builtin.sh 25 | 26 | # .FUNCTION rand.random -> [uint]|[bool] 27 | # 28 | # Gera um inteiro pseudo-aleatório entre 0 e 32767. 29 | # 30 | function rand.random() 31 | { 32 | getopt.parse 0 "$@" 33 | echo $RANDOM 34 | return $? 35 | } 36 | 37 | # .FUNCTION rand.range -> [int]|[bool] 38 | # 39 | # Retorna um número inteiro pseudo-aleatório 40 | # dentro do intervalo especificado. 41 | # 42 | function rand.range() 43 | { 44 | getopt.parse 2 "min:int:$1" "max:int:$2" "${@:3}" 45 | 46 | shuf -i $1-$2 -n1 47 | return $? 48 | } 49 | 50 | # .FUNCTION rand.choice -> [str]|[bool] 51 | # 52 | # Escolhe um elemento aleatório de uma sequência. 53 | # 54 | function rand.choice() 55 | { 56 | getopt.parse 1 "list:array:$1" "${@:2}" 57 | 58 | local -n __ref__=$1 59 | local __list__ 60 | 61 | [[ ${__ref__[@]} ]] && 62 | __list__=("${__ref__[@]}") && 63 | echo "${__list__[$(shuf -i 0-$((${#__list__[@]}-1)) -n1)]}" 64 | 65 | return $? 66 | } 67 | 68 | # .FUNCTION rand.shuffle -> [bool] 69 | # 70 | # Embaralha os elementos da lista. 71 | # 72 | function rand.shuffle() 73 | { 74 | getopt.parse 1 "list:array:$1" "${@:2}" 75 | 76 | local -n __ref__=$1 77 | mapfile $1 < <(printf '%s\n' "${__ref__[@]}" | shuf) 78 | return $? 79 | } 80 | 81 | # .TYPE rand_t 82 | # 83 | # Implementa o objeto 'S' com os métodos: 84 | # 85 | # S.choice 86 | # S.shuffle 87 | # 88 | typedef rand_t rand.choice \ 89 | rand.shuffle 90 | 91 | # Funções (somente-leitura) 92 | readonly -f rand.random \ 93 | rand.range \ 94 | rand.choice \ 95 | rand.shuffle 96 | 97 | # /* __RAND_SH__ */ 98 | -------------------------------------------------------------------------------- /src/regex.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __REGEX_SH__ ] && return 0 21 | 22 | readonly __REGEX_SH__=1 23 | 24 | source builtin.sh 25 | 26 | readonly -A __REGEX__=( 27 | [groupname]="\(\?<(${__BUILTIN__[objname]})>.+\)" 28 | ) 29 | 30 | # .FUNCTION regex.compile -> [bool] 31 | # 32 | # Retorna 'true' se o padrão compilado satisfaz a sintaxe 33 | # POSIX da expressão regular estendida. 34 | # 35 | function regex.compile() 36 | { 37 | getopt.parse 1 "pattern:str:$1" "${@:2}" 38 | 39 | [[ _ =~ $1 ]] 40 | [[ $? -eq 2 ]] && error.fatal "'$1' erro de sintaxe na expressão regular" 41 | 42 | return 0 43 | } 44 | 45 | # .FUNCTION regex.findall -> [str]|[bool] 46 | # 47 | # Retorna uma lista de todas as correspondências não sobrepostas na cadeia. 48 | # 49 | function regex.findall() 50 | { 51 | getopt.parse 2 "pattern:str:$1" "expr:str:$2" "${@:3}" 52 | 53 | local expr=$2 54 | 55 | while [[ $1 && $expr =~ $1 ]]; do 56 | echo "$BASH_REMATCH" 57 | expr=${expr/$BASH_REMATCH/} 58 | done 59 | 60 | return $? 61 | } 62 | 63 | # .FUNCTION regex.fullmatch -> [bool] 64 | # 65 | # Força o padrão a casar com a expressão inteira. 66 | # 67 | function regex.fullmatch() 68 | { 69 | getopt.parse 3 "pattern:str:$1" "expr:str:$2" "match:map:$3" "${@:4}" 70 | 71 | local -n __ref__=$3 72 | 73 | __ref__=() || return 1 74 | 75 | if [[ $2 =~ ^$1$ ]]; then 76 | __ref__[start]=0 77 | __ref__[end]=${#BASH_REMATCH} 78 | __ref__[match]=$BASH_REMATCH 79 | fi 80 | 81 | return $? 82 | } 83 | 84 | # .FUNCTION regex.match -> [bool] 85 | # 86 | # Força o padrão a casar com a expressão inicial. 87 | # 88 | function regex.match() 89 | { 90 | getopt.parse 3 "pattern:str:$1" "expr:str:$2" "match:map:$3" "${@:4}" 91 | 92 | local -n __ref__=$3 93 | 94 | __ref__=() || return 1 95 | 96 | if [[ $2 =~ ^$1 ]]; then 97 | __ref__[start]=0 98 | __ref__[end]=${#BASH_REMATCH} 99 | __ref__[match]=$BASH_REMATCH 100 | fi 101 | 102 | return $? 103 | } 104 | 105 | # .FUNCTION regex.search -> [bool] 106 | # 107 | # Busca uma correspondência do padrão na expressão. 108 | # 109 | function regex.search() 110 | { 111 | getopt.parse 3 "pattern:str:$1" "expr:str:$2" "match:map:$3" "${@:4}" 112 | 113 | local __start__ __end__ __match__ 114 | local __expr__=$2 115 | local -n __ref__=$3 116 | 117 | __ref__=() || return 1 118 | 119 | if [[ $__expr__ =~ $1 ]]; then 120 | __expr__=${__expr__%$BASH_REMATCH*} 121 | __ref__[start]=${#__expr__} 122 | __ref__[end]=$((${__ref__[start]}+${#BASH_REMATCH})) 123 | __ref__[match]=$BASH_REMATCH 124 | fi 125 | 126 | return $? 127 | } 128 | 129 | # .FUNCTION regex.split -> [str]|[bool] 130 | # 131 | # Divide a string de origem pelas ocorrências do padrão, retornando uma lista 132 | # contendo as substrings resultantes. 133 | # 134 | function regex.split() 135 | { 136 | getopt.parse 2 "pattern:str:$1" "expr:str:$2" "${@:3}" 137 | 138 | local expr=$2 139 | 140 | while [[ $1 && $expr =~ $1 ]]; do 141 | expr=${expr/$BASH_REMATCH/$'\n'} 142 | done 143 | 144 | echo "$expr" 145 | 146 | return $? 147 | } 148 | 149 | # .FUNCTION regex.groups -> [str]|[bool] 150 | # 151 | # Retorna uma lista de todos os grupos de captura presentes na ocorrência. 152 | # 153 | function regex.groups() 154 | { 155 | getopt.parse 2 "pattern:str:$1" "expr:str:$2" "${@:3}" 156 | 157 | local expr=$2 158 | 159 | while [[ $expr =~ $1 && ${BASH_REMATCH[1]} ]]; do 160 | printf '%s\n' "${BASH_REMATCH[@]:1}" 161 | expr=${expr/$BASH_REMATCH/} 162 | done 163 | 164 | return $? 165 | } 166 | 167 | # .FUNCTION regex.replace -> [str]|[bool] 168 | # 169 | # Substitui 'N' ocorrências do padrão casado pela string especificada. 170 | # Se 'count < 0' aplica a substituição em todas as ocorrências. 171 | # 172 | function regex.replace() 173 | { 174 | getopt.parse 4 "pattern:str:$1" "expr:str:$2" "count:int:$3" "new:str:$4" "${@:5}" 175 | 176 | local i c 177 | local expr=$2 178 | 179 | for ((i=0; i < ${#expr}; i++)); do 180 | [[ ${expr:$i} =~ $1 ]] || break 181 | if [[ ${expr:$i:${#BASH_REMATCH}} == $BASH_REMATCH ]]; then 182 | expr=${expr:0:$i}${4}${expr:$(($i+${#BASH_REMATCH}))} 183 | i=$(($i+${#4}-1)) 184 | [[ $((++c)) -eq $3 ]] && break 185 | fi 186 | done 187 | 188 | echo "$expr" 189 | 190 | return $? 191 | } 192 | 193 | # .FUNCTION regex.fnreplace ... -> [str]|[bool] 194 | # 195 | # Substitui 'N' ocorrências do padrão pelo retorno da função com 'N'args (opcional), passando como 196 | # como argumento posicional '$1' o padrão casado. Se 'count < 0' aplica a substtiuição em todas as 197 | # ocorrências. 198 | # 199 | # == EXEMPLO == 200 | # 201 | # source regex.sh 202 | # 203 | # dobrar() 204 | # { 205 | # # Retorna o dobro do valor casado. 206 | # echo "$(($1*2))" 207 | # } 208 | # 209 | # expr='valor_a = 10, valor_b = 20, valor_c = 30' 210 | # 211 | # # Valor atual. 212 | # echo "$expr" 213 | # echo --- 214 | # 215 | # # Substitui somente os números contidos na expressão. 216 | # regex.fnreplace '[0-9]+' "$expr" 1 dobrar # 1ª ocorrência 217 | # regex.fnreplace '[0-9]+' "$expr" 2 dobrar # 1ª e 2ª ocorrência 218 | # regex.fnreplace '[0-9]+' "$expr" -1 dobrar # Todas 219 | # 220 | # == SAÍDA == 221 | # 222 | # valor_a = 10, valor_b = 20, valor_c = 30 223 | # --- 224 | # valor_a = 20, valor_b = 20, valor_c = 30 225 | # valor_a = 20, valor_b = 40, valor_c = 30 226 | # valor_a = 20, valor_b = 40, valor_c = 60 227 | # 228 | function regex.fnreplace() 229 | { 230 | getopt.parse -1 "pattern:str:$1" "expr:str:$2" "count:int:$3" "func:function:$4" "args:str:$5" ... "${@:6}" 231 | 232 | local new i c 233 | local expr=$2 234 | 235 | for ((i=0; i < ${#expr}; i++)); do 236 | [[ ${expr:$i} =~ $1 ]] || break 237 | if [[ ${expr:$i:${#BASH_REMATCH}} == $BASH_REMATCH ]]; then 238 | new=$($4 "$BASH_REMATCH" "${@:5}") 239 | expr=${expr:0:$i}${new}${expr:$(($i+${#BASH_REMATCH}))} 240 | i=$(($i+${#new}-1)) 241 | [[ $((++c)) -eq $3 ]] && break 242 | fi 243 | done 244 | 245 | echo "$expr" 246 | 247 | return $? 248 | } 249 | 250 | # .FUNCTION regex.fnreplacers -> [str]|[bool] 251 | # 252 | # Retorna uma string substituindo 'N' ocorrências do padrão pelo retorno da função, passando os grupos 253 | # casados como argumentos posicionais. Se 'count < 0' aplica a substituição em todas as ocorrências. 254 | # 255 | # Exemplo: ([a-z]+)([A-Z]+)([0-9]) ... (...) 256 | # | | | | 257 | # grupo1 grupo2 grupo3 ... grupoN 258 | # | | | | 259 | # func $1 $2 $3 $N 260 | # 261 | # > Chama a função se pelo menos um grupo de captura estiver presente. 262 | # 263 | # == EXEMPLO == 264 | # 265 | # source regex.sh 266 | # 267 | # grupos() 268 | # { 269 | # # Retorna a ordem invertida dos grupos de captura. 270 | # # 271 | # # $1 = '16' 272 | # # $2 = ' de julho de ' 273 | # # $3 = '1993' 274 | # echo "${3}${2}${1}" 275 | # } 276 | # 277 | # texto='Slackware é uma distribuição Linux lançada em 16 de julho de 1993 por Patrick Volkerding' 278 | # 279 | # echo "$texto" 280 | # regex.fnreplacers "([0-9]+)(.+\s+)([0-9]+)" "$texto" -1 grupos 281 | # 282 | # == SAÍDA == 283 | # 284 | # Slackware é uma distribuição Linux lançada em 16 de julho de 1993 por Patrick Volkerding 285 | # Slackware é uma distribuição Linux lançada em 1993 de julho de 16 por Patrick Volkerding 286 | # 287 | function regex.fnreplacers() 288 | { 289 | getopt.parse 4 "pattern:str:$1" "expr:str:$2" "count:int:$3" "func:function:$4" "${@:5}" 290 | 291 | local new i c 292 | local expr=$2 293 | 294 | for ((i=0; i < ${#expr}; i++)); do 295 | [[ ${expr:$i} =~ $1 ]] || break 296 | if [[ ${expr:$i:${#BASH_REMATCH}} == $BASH_REMATCH && ${BASH_REMATCH[1]} ]]; then 297 | new=$($4 "${BASH_REMATCH[@]:1}") 298 | expr=${expr:0:$i}${new}${expr:$(($i+${#BASH_REMATCH}))} 299 | i=$(($i+${#new}-1)) 300 | [[ $((++c)) -eq $3 ]] && break 301 | fi 302 | done 303 | 304 | echo "$expr" 305 | 306 | return $? 307 | } 308 | 309 | # .FUNCTION regex.expand -> [str]|[bool] 310 | # 311 | # Expande o grupo nomeado para o seu padrão casado no modelo especificado. 312 | # O padrão para definição de nomenclatura de grupo deve respeitar a seguinte sintaxe: 313 | # 314 | # (?regex) ... 315 | # 316 | # group_name - Identificador do grupo cujo caracteres suportados são: '[a-zA-Z0-9_]' e 317 | # precisa iniciar com pelo menos uma letra ou underline '_'. 318 | # regex - Expressão regular estendida. 319 | # 320 | # > Pode ser especificado mais de um grupo. 321 | # 322 | # O modelo é uma cadeia de caracteres que compõe a formatação dos grupos 323 | # nomeados cuja expansão é aplicada ao seu identificador representado pela 324 | # sintaxe: 325 | # 326 | # 327 | # 328 | # == EXEMPLO == 329 | # 330 | # source regex.sh 331 | # 332 | # # Modelo. 333 | # modelo=$(cat << _eof 334 | # Nome: 335 | # Sobrenome: 336 | # Idade: 337 | # Cidade: 338 | # Estado: 339 | # _eof 340 | # ) 341 | # 342 | # Dados a serem extraidos. 343 | # dados='Fernanda,Santos,30,Volta Redonda,RJ' 344 | # 345 | # # Expressão regular que define os grupos nomeados para cada campo. 346 | # re='^(?\w+),(?\w+),(?[0-9]+),(?[a-zA-Z0-9 ]+),(?[a-zA-Z]{2})$' 347 | # 348 | # # Retorna o modelo expandindo os padrões casados. 349 | # regex.expand "$re" "$dados" "$modelo" 350 | # 351 | # == SAÍDA == 352 | # 353 | # Nome: Fernanda 354 | # Sobrenome: Santos 355 | # Idade: 30 356 | # Cidade: Volta Redonda 357 | # Estado: RJ 358 | # 359 | function regex.expand() 360 | { 361 | getopt.parse 3 "pattern:str:$1" "expr:str:$2" "template:str:$3" "${@:4}" 362 | 363 | local name names i 364 | local pattern=$1 365 | local template=$3 366 | 367 | # Extrai os nomes associados as expressões do grupo. 368 | while [[ $pattern =~ ${__REGEX__[groupname]} ]]; do 369 | # Anexa o nome e atualiza a expressão para o padrão POSIX ERE 370 | # removendo os identificadores. 371 | names+=(${BASH_REMATCH[1]}) 372 | pattern=${pattern/\?<${BASH_REMATCH[1]}>/} 373 | done 374 | 375 | if [[ $2 =~ $pattern ]]; then 376 | for name in ${names[@]}; do 377 | # Substitui os nomes por suas respectivas ocorrêncicas. 378 | template=${template//<$name>/${BASH_REMATCH[$((++i))]}} 379 | done 380 | fi 381 | 382 | echo "$template" 383 | 384 | return $? 385 | } 386 | 387 | # .FUNCTION regex.exportnames -> [bool] 388 | # 389 | # Exporta os grupos nomeados e atribui os padrões casados. 390 | # O padrão para definição de nomenclatura de grupo deve respeitar a seguinte sintaxe: 391 | # 392 | # (?regex) ... 393 | # 394 | # group_name - Identificador do grupo cujo caracteres suportados são: '[a-zA-Z0-9_]' e 395 | # precisa iniciar com pelo menos uma letra ou underline '_'. 396 | # regex - Expressão regular estendida. 397 | # 398 | # > Pode ser especificado mais de um grupo. 399 | # 400 | # == EXEMPLO == 401 | # 402 | # source regex.sh 403 | # 404 | # dados='Patrick Volkerding' 405 | # 406 | # regex.exportnames '(?\w+) (?\w+)' "$dados" 407 | # 408 | # echo "Nome:" $nome 409 | # echo "Sobrenome:" $sobrenome 410 | # 411 | # == SAÍDA == 412 | # 413 | # Nome: Patrick 414 | # Sobrenome: Volkerding 415 | # 416 | function regex.exportnames() 417 | { 418 | getopt.parse 2 "pattern:str:$1" "expr:str:$2" "${@:3}" 419 | 420 | local __name__ __names__ __i__ 421 | local __pattern__=$1 422 | 423 | while [[ $__pattern__ =~ ${__REGEX__[groupname]} ]]; do 424 | __names__+=(${BASH_REMATCH[1]}) 425 | __pattern__=${__pattern__/\?<${BASH_REMATCH[1]}>/} 426 | done 427 | 428 | if [[ $2 =~ $__pattern__ ]]; then 429 | for __name__ in ${__names__[@]}; do 430 | # Atribui o valor ao identificador. 431 | printf -v $__name__ "${BASH_REMATCH[$((++__i__))]}" 432 | done 433 | fi 434 | 435 | return $? 436 | } 437 | 438 | # .MAP match 439 | # 440 | # Chaves: 441 | # 442 | # start 443 | # end 444 | # match 445 | # 446 | 447 | # .TYPE regex_t 448 | # 449 | # Implementa o objeto 'S' com os métodos: 450 | # 451 | # S.compile 452 | # S.findall 453 | # S.fullmatch 454 | # S.match 455 | # S.search 456 | # S.split 457 | # S.groups 458 | # S.replace 459 | # S.fnreplace 460 | # S.fnreplacers 461 | # S.expand 462 | # S.exportnames 463 | # 464 | typedef regex_t \ 465 | regex.compile \ 466 | regex.findall \ 467 | regex.fullmatch \ 468 | regex.match \ 469 | regex.search \ 470 | regex.split \ 471 | regex.groups \ 472 | regex.replace \ 473 | regex.fnreplace \ 474 | regex.fnreplacers \ 475 | regex.expand \ 476 | regex.exportnames 477 | 478 | readonly -f regex.compile \ 479 | regex.findall \ 480 | regex.fullmatch \ 481 | regex.match \ 482 | regex.search \ 483 | regex.split \ 484 | regex.groups \ 485 | regex.replace \ 486 | regex.fnreplace \ 487 | regex.fnreplacers \ 488 | regex.expand \ 489 | regex.exportnames 490 | 491 | # /* __REGEX_SH__ */ 492 | -------------------------------------------------------------------------------- /src/setup.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __SETUP_SH__ ] && return 0 21 | 22 | readonly __SETUP_SH__=1 23 | 24 | source builtin.sh 25 | 26 | # .FUNCTION setup.package ... [bool] 27 | # 28 | # Verifica dependências e critérios de versionamento de pacotes e binários 29 | # em um arquivo fonte. Retorna 'true' de todos os critérios forem satisfeitos, 30 | # caso contrário retorna 'false' e finaliza o script com status '1'. 31 | # 32 | # A expressão deve respeitar a seguinte sintaxe: 33 | # 34 | # 'package (operator version, ...)' 35 | # 36 | # --- 37 | # package - Nome interno do pacote. 38 | # operator - Operador de comparação. São suportados: (>, <, >=, <=, !=, =). 39 | # version - Inteiros sem sinal delimitados por '.' 40 | # --- 41 | # > Pode ser especiifcado mais de um pacote. 42 | # > Pode ser especificada mais de uma operação de versionamento e deve 43 | # ser delimitada por uma ',' vírgula. 44 | # 45 | # == EXEMPLO == 46 | # 47 | # source setup.sh 48 | # 49 | # # No pacote 'curl' as versões entre '4.0' (inclusive) e '7.5' são compatíveis. (exceto: 4.3) 50 | # setup.package 'curl (>= 4.0, < 7.5, != 4.3)' \ 51 | # ' jq (>= 1.5)' 52 | # 53 | function setup.package() 54 | { 55 | getopt.parse -1 "package:str:$1" ... "${@:2}" 56 | 57 | local pkg cond op ver iver ret pkgname expr pkgtool reg_syn reg_ver 58 | 59 | # Sintaxe de versionamento. 60 | reg_syn='^([a-zA-Z0-9_-]+)\s+\((\s*(>|<|>=|<=|!=|==)\s*[0-9]+(.[0-9]+)*\s*(,\s*(>|<|>=|<=|!=|==)\s*[0-9]+(.[0-9]+)*\s*)*)\)$' 61 | reg_ver='[0-9]+(.[0-9]+)+' 62 | 63 | # Obtem o gerenciador de pacotes. 64 | for pkgtool in apt-get dnf yum pacman zypper slackpkg :; do 65 | which $pkgtool && break 66 | done 1>/dev/null 67 | 68 | # Trata as versões dos pacotes. 69 | for pkg in "$@"; do 70 | [[ $pkg =~ $reg_syn ]] || error.fatalf "'$pkg' erro de sintaxe\nSource: ${BASH_SOURCE[-2]}\n" 71 | pkgname=${BASH_REMATCH[1]} # Nome do pacote. 72 | expr=${BASH_REMATCH[2]} # Condicional de versionamento. 73 | 74 | type -f $pkgname &>/dev/null || error.fatal "'$pkgname' o pacote ou binário requerido está ausente" 75 | 76 | # Obtêm versionamento via gerenciador de pacotes. 77 | case $pkgtool in 78 | apt-get) iver=$(dpkg-query -W -f '${Version}' $pkgname);; 79 | pacman) iver=$(pacman -Qi $pkgname | awk -F: '/^Version/ {print $2}');; 80 | slackpkg) iver=$(slackpkg info $pkgname | awk -F: '/^PACKAGE NAME/ {print $2}');; 81 | dnf|yum|zypper) iver=$($pkgtool info $pkgname | awk -F: '/^Version/ {print $2}');; 82 | esac 2>/dev/null 83 | 84 | # Força obter a versão via linha de comando para binários compilados. 85 | iver=${iver:-$($pkgname --version)} 86 | 87 | [[ $iver =~ $reg_ver ]] || error.fatal "'$pkgname' não foi possível obter a versão do pacote ou binário" 88 | iver=$BASH_REMATCH # Versão do pacote instalado. 89 | 90 | # Lê a expressão condicional referente e obtém os 91 | # operadores de comparação e versionamento. 92 | while IFS=$'\n' read cond; do 93 | IFS=' ' read op ver <<< "$cond" 94 | # Testa o versionamento. 95 | awk "BEGIN { exit $iver $op $ver }" 96 | ret+=($?) # Anexa status de retorno. 97 | done <<< "${expr//,/$'\n'}" 98 | 99 | ret=${ret[@]} 100 | ((${ret// /&})) || error.fatalf "'$pkgname' a versão instalada é incompatível.\nSource: ${BASH_SOURCE[-2]}\nRequer: ${pkg//+( )/ }\n" 101 | done 102 | 103 | return $? 104 | } 105 | 106 | # /* __SETUP_SH__ */ 107 | -------------------------------------------------------------------------------- /src/ssh.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __SSH_SH__ ] && return 0 21 | 22 | readonly __SSH_SH__=1 23 | 24 | source builtin.sh 25 | source struct.sh 26 | 27 | # .FUNCTION ssh.new -> [bool] 28 | # 29 | # Cria uma nova sessão com as configurações especificadas na estrutura e salva 30 | # no objeto apontado por 'session'. 31 | # 32 | function ssh.new() 33 | { 34 | getopt.parse 2 "session:ssh_t:$1" "config:ssh_config_st:$2" "${@:3}" 35 | 36 | local __user__=$($2.user) \ 37 | __pass__=$($2.pass) \ 38 | __host__=$($2.host) \ 39 | __port__=$($2.port) \ 40 | __forward_auth__=$($2.forward_auth) \ 41 | __bind_address__=$($2.bind_address) \ 42 | __log_file__=$($2.log_file) \ 43 | __config_file__=$($2.config_file) \ 44 | __add_keys_to_agent__=$($2.add_keys_to_agent) \ 45 | __address_family__=$($2.address_family) \ 46 | __batch_mode__=$($2.batch_mode) \ 47 | __canonical_domains__=$($2.canonical_domains) \ 48 | __canonicalize_fallback_local__=$($2.canonicalize_fallback_local) \ 49 | __canonicalize_hostname__=$($2.canonicalize_hostname) \ 50 | __canonicalize_max_dots__=$($2.canonicalize_max_dots) \ 51 | __canonicalize_permitted_cnames__=$($2.canonicalize_permitted_cnames) \ 52 | __cetificate_file__=$($2.cetificate_file) \ 53 | __challenge_response_authentication__=$($2.challenge_response_authentication) \ 54 | __check_host_ip__=$($2.check_host_ip) \ 55 | __ciphers__=$($2.ciphers) \ 56 | __clear_all_forwardings__=$($2.clear_all_forwardings) \ 57 | __escape_char__=$($2.escape_char) \ 58 | __compression__=$($2.compression) \ 59 | __compression_level__=$($2.compression_level) \ 60 | __connection_attempts__=$($2.connection_attempts) \ 61 | __connect_timeout__=$($2.connect_timeout) \ 62 | __control_master__=$($2.control_master) \ 63 | __control_path__=$($2.control_path) \ 64 | __control_persist__=$($2.control_persist) \ 65 | __dynamic_forward__=$($2.dynamic_forward) \ 66 | __enable_ssh_keysing__=$($2.enable_ssh_keysing) \ 67 | __exit_on_forward_failure__=$($2.exit_on_forward_failure) \ 68 | __finger_print_hash__=$($2.finger_print_hash) \ 69 | __forward_agent__=$($2.forward_agent) \ 70 | __forward_x11__=$($2.forward_x11) \ 71 | __forward_x11_timeout__=$($2.forward_x11_timeout) \ 72 | __forward_x11_trusted__=$($2.forward_x11_trusted) \ 73 | __gateway_ports__=$($2.gateway_ports) \ 74 | __global_known_hosts_file__=$($2.global_known_hosts_file) \ 75 | __gssapi_authentication__=$($2.gssapi_authentication) \ 76 | __gssapi_key_exchange__=$($2.gssapi_key_exchange) \ 77 | __gssapi_client_identity__=$($2.gssapi_client_identity) \ 78 | __gssapi_server_identity__=$($2.gssapi_server_identity) \ 79 | __gssapi_delegate_credentials__=$($2.gssapi_delegate_credentials) \ 80 | __gssapi_renewal_forces_rekey__=$($2.gssapi_renewal_forces_rekey) \ 81 | __gssapi_trust_dns__=$($2.gssapi_trust_dns) \ 82 | __hash_known_hosts__=$($2.hash_known_hosts) \ 83 | __host_based_authentication__=$($2.host_based_authentication) \ 84 | __host_based_key_types__=$($2.host_based_key_types) \ 85 | __host_key_algorithms__=$($2.host_key_algorithms) \ 86 | __host_key_alias__=$($2.host_key_alias) \ 87 | __hostname__=$($2.hostname) \ 88 | __identities_only__=$($2.identities_only) \ 89 | __identity_agent__=$($2.identity_agent) \ 90 | __identity_file__=$($2.identity_file) \ 91 | __ignore_unknown__=$($2.ignore_unknown) \ 92 | __include__=$($2.include) \ 93 | __ip_qos__=$($2.ip_qos) \ 94 | __kbd_interactive_authentication__=$($2.kbd_interactive_authentication) \ 95 | __kbd_interactive_devices__=$($2.kbd_interactive_devices) \ 96 | __kex_algorithms__=$($2.kex_algorithms) \ 97 | __local_command__=$($2.local_command) \ 98 | __local_forward__=$($2.local_forward) \ 99 | __log_level__=$($2.log_level) \ 100 | __macs__=$($2.macs) \ 101 | __no_host_authentication_for_local_host__=$($2.no_host_authentication_for_local_host) \ 102 | __number_of_password_prompts__=$($2.number_of_password_prompts) \ 103 | __password_authentication__=$($2.password_authentication) \ 104 | __permit_local_command__=$($2.permit_local_command) \ 105 | __pkcs11_provider__=$($2.pkcs11_provider) \ 106 | __preferred_authentications__=$($2.preferred_authentications) \ 107 | __proxy_command__=$($2.proxy_command) \ 108 | __proxy_jump__=$($2.proxy_jump) \ 109 | __proxy_use_fdpass__=$($2.proxy_use_fdpass) \ 110 | __pubkey_accepted_key_types__=$($2.pubkey_accepted_key_types) \ 111 | __pubkey_authentication__=$($2.pubkey_authentication) \ 112 | __rekey_limit__=$($2.rekey_limit) \ 113 | __remote_forward__=$($2.remote_forward) \ 114 | __remote_command__=$($2.remote_command) \ 115 | __request_tty__=$($2.request_tty) \ 116 | __revoked_host_keys__=$($2.revoked_host_keys) \ 117 | __send_env__=$($2.send_env) \ 118 | __server_alive_count_max__=$($2.server_alive_count_max) \ 119 | __server_alive_interval__=$($2.server_alive_interval) \ 120 | __stream_local_bind_mask__=$($2.stream_local_bind_mask) \ 121 | __stream_local_bind_unlink__=$($2.stream_local_bind_unlink) \ 122 | __strict_host_key_checking__=$($2.strict_host_key_checking) \ 123 | __tcp_keep_alive__=$($2.tcp_keep_alive) \ 124 | __tunnel__=$($2.tunnel) \ 125 | __tunnel_device__=$($2.tunnel_device) \ 126 | __update_host_keys__=$($2.update_host_keys) \ 127 | __use_privileged_port__=$($2.use_privileged_port) \ 128 | __user_known_hosts_file__=$($2.user_known_hosts_file) \ 129 | __verify_host_key_dns__=$($2.verify_host_key_dns) \ 130 | __visual_host_key__=$($2.visual_host_key) \ 131 | __xauth_location__=$($2.xauth_location) \ 132 | __sha__ \ 133 | __aes__ \ 134 | __ssh_opts__ 135 | 136 | # Configurações 137 | __ssh_opts__+=${__add_keys_to_agent__:+-o AddKeysToAgent=$__add_keys_to_agent__ } 138 | __ssh_opts__+=${__address_family__:+-o AddressFamily=$__address_family__ } 139 | __ssh_opts__+=${__batch_mode__:+-o BatchMode=$__batch_mode__ } 140 | __ssh_opts__+=${__bind_address__:+-o BindAddress=$__bind_address__ } 141 | __ssh_opts__+=${__canonical_domains__:+-o CanonicalDomains=$__canonical_domains__ } 142 | __ssh_opts__+=${__canonicalize_fallback_local__:+-o CanonicalizeFallbackLocal=$__canonicalize_fallback_local__ } 143 | __ssh_opts__+=${__canonicalize_hostname__:+-o CanonicalizeHostname=$__canonicalize_hostname__ } 144 | __ssh_opts__+=${__canonicalize_max_dots__:+-o CanonicalizeMaxDots=$__canonicalize_max_dots__ } 145 | __ssh_opts__+=${__canonicalize_permitted_cnames__:+-o CanonicalizePermittedCNAMEs=$__canonicalize_permitted_cnames__ } 146 | __ssh_opts__+=${__cetificate_file__:+-o CertificateFile=$__cetificate_file__ } 147 | __ssh_opts__+=${__challenge_response_authentication__:+-o ChallengeResponseAuthentication=$__challenge_response_authentication__ } 148 | __ssh_opts__+=${__check_host_ip__:+-o CheckHostIP=$__check_host_ip__ } 149 | __ssh_opts__+=${__ciphers__:+-o Ciphers=$__ciphers__ } 150 | __ssh_opts__+=${__clear_all_forwardings__:+-o ClearAllForwardings=$__clear_all_forwardings__ } 151 | __ssh_opts__+=${__compression_level__:+-o $__compression_level__ } 152 | __ssh_opts__+=${__compression__:+-o Compression=$__compression__ } 153 | __ssh_opts__+=${__config_file__:+-F $__config_file__ } 154 | __ssh_opts__+=${__connection_attempts__:+-o ConnectionAttempts=$__connection_attempts__ } 155 | __ssh_opts__+=${__connect_timeout__:+-o ConnectTimeout=$__connect_timeout__ } 156 | __ssh_opts__+=${__control_master__:+-o ControlMaster=$__control_master__ } 157 | __ssh_opts__+=${__control_path__:+-o ControlPath=$__control_path__ } 158 | __ssh_opts__+=${__control_persist__:+-o ControlPersist=$__control_persist__ } 159 | __ssh_opts__+=${__dynamic_forward__:+-o DynamicForward=$__dynamic_forward__ } 160 | __ssh_opts__+=${__enable_ssh_keysing__:+-o EnableSSHKeysign=$__enable_ssh_keysing__ } 161 | __ssh_opts__+=${__escape_char__:+-o EscapeChar=$__escape_char__ } 162 | __ssh_opts__+=${__exit_on_forward_failure__:+-o ExitOnForwardFailure=$__exit_on_forward_failure__ } 163 | __ssh_opts__+=${__finger_print_hash__:+-o FingerprintHash=$__finger_print_hash__ } 164 | __ssh_opts__+=${__forward_agent__:+-o ForwardAgent=$__forward_agent__ } 165 | __ssh_opts__+=${__forward_auth__:+-o $__forward_auth__ } 166 | __ssh_opts__+=${__forward_x11__:+-o ForwardX11=$__forward_x11__ } 167 | __ssh_opts__+=${__forward_x11_timeout__:+-o ForwardX11Timeout=$__forward_x11_timeout__ } 168 | __ssh_opts__+=${__forward_x11_trusted__:+-o ForwardX11Trusted=$__forward_x11_trusted__ } 169 | __ssh_opts__+=${__gateway_ports__:+-o GatewayPorts=$__gateway_ports__ } 170 | __ssh_opts__+=${__global_known_hosts_file__:+-o GlobalKnownHostsFile=$__global_known_hosts_file__ } 171 | __ssh_opts__+=${__gssapi_authentication__:+-o GSSAPIAuthentication=$__gssapi_authentication__ } 172 | __ssh_opts__+=${__gssapi_client_identity__:+-o GSSAPIClientIdentity=$__gssapi_client_identity__ } 173 | __ssh_opts__+=${__gssapi_delegate_credentials__:+-o GSSAPIDelegateCredentials=$__gssapi_delegate_credentials__ } 174 | __ssh_opts__+=${__gssapi_key_exchange__:+-o GSSAPIKeyExchange=$__gssapi_key_exchange__ } 175 | __ssh_opts__+=${__gssapi_renewal_forces_rekey__:+-o GSSAPIRenewalForcesRekey=$__gssapi_renewal_forces_rekey__ } 176 | __ssh_opts__+=${__gssapi_server_identity__:+-o GSSAPIServerIdentity=$__gssapi_server_identity__ } 177 | __ssh_opts__+=${__gssapi_trust_dns__:+-o GSSAPITrustDns=$__gssapi_trust_dns__ } 178 | __ssh_opts__+=${__hash_known_hosts__:+-o HashKnownHosts=$__hash_known_hosts__ } 179 | __ssh_opts__+=${__host_based_authentication__:+-o HostbasedAuthentication=$__host_based_authentication__ } 180 | __ssh_opts__+=${__host_based_key_types__:+-o HostbasedKeyTypes=$__host_based_key_types__ } 181 | __ssh_opts__+=${__host_key_algorithms__:+-o HostKeyAlgorithms=$__host_key_algorithms__ } 182 | __ssh_opts__+=${__host_key_alias__:+-o HostKeyAlias=$__host_key_alias__ } 183 | __ssh_opts__+=${__hostname__:+-o HostName=$__hostname__ } 184 | __ssh_opts__+=${__identities_only__:+-o IdentitiesOnly=$__identities_only__ } 185 | __ssh_opts__+=${__identity_agent__:+-o IdentityAgent=$__identity_agent__ } 186 | __ssh_opts__+=${__identity_file__:+-o IdentityFile=$__identity_file__ } 187 | __ssh_opts__+=${__ignore_unknown__:+-o IgnoreUnknown=$__ignore_unknown__ } 188 | __ssh_opts__+=${__include__:+-o Include=$__include__ } 189 | __ssh_opts__+=${__ip_qos__:+-o IPQoS=$__ip_qos__ } 190 | __ssh_opts__+=${__kbd_interactive_authentication__:+-o KbdInteractiveAuthentication=$__kbd_interactive_authentication__ } 191 | __ssh_opts__+=${__kbd_interactive_devices__:+-o KbdInteractiveDevices=$__kbd_interactive_devices__ } 192 | __ssh_opts__+=${__kex_algorithms__:+-o KexAlgorithms=$__kex_algorithms__ } 193 | __ssh_opts__+=${__local_command__:+-o LocalCommand=$__local_command__ } 194 | __ssh_opts__+=${__local_forward__:+-o LocalForward=$__local_forward__ } 195 | __ssh_opts__+=${__log_file__:+-E $__log_file__ } 196 | __ssh_opts__+=${__log_level__:+-o LogLevel=$__log_level__ } 197 | __ssh_opts__+=${__macs__:+-o MACs=$__macs__ } 198 | __ssh_opts__+=${__no_host_authentication_for_local_host__:+-o NoHostAuthenticationForLocalhost=$__no_host_authentication_for_local_host__ } 199 | __ssh_opts__+=${__number_of_password_prompts__:+-o NumberOfPasswordPrompts=$__number_of_password_prompts__ } 200 | __ssh_opts__+=${__password_authentication__:+-o PasswordAuthentication=$__password_authentication__ } 201 | __ssh_opts__+=${__permit_local_command__:+-o PermitLocalCommand=$__permit_local_command__ } 202 | __ssh_opts__+=${__pkcs11_provider__:+-o PKCS11Provider=$__pkcs11_provider__ } 203 | __ssh_opts__+=${__port__:+-o Port=$__port__ } 204 | __ssh_opts__+=${__preferred_authentications__:+-o PreferredAuthentications=$__preferred_authentications__ } 205 | __ssh_opts__+=${__proxy_command__:+-o ProxyCommand=$__proxy_command__ } 206 | __ssh_opts__+=${__proxy_jump__:+-o ProxyJump=$__proxy_jump__ } 207 | __ssh_opts__+=${__proxy_use_fdpass__:+-o ProxyUseFdpass=$__proxy_use_fdpass__ } 208 | __ssh_opts__+=${__pubkey_accepted_key_types__:+-o PubkeyAcceptedKeyTypes=$__pubkey_accepted_key_types__ } 209 | __ssh_opts__+=${__pubkey_authentication__:+-o PubkeyAuthentication=$__pubkey_authentication__ } 210 | __ssh_opts__+=${__rekey_limit__:+-o RekeyLimit=$__rekey_limit__ } 211 | __ssh_opts__+=${__remote_command__:+-o RemoteCommand=$__remote_command__ } 212 | __ssh_opts__+=${__remote_forward__:+-o RemoteForward=$__remote_forward__ } 213 | __ssh_opts__+=${__request_tty__:+-o RequestTTY=$__request_tty__ } 214 | __ssh_opts__+=${__revoked_host_keys__:+-o RevokedHostKeys=$__revoked_host_keys__ } 215 | __ssh_opts__+=${__send_env__:+-o SendEnv=$__send_env__ } 216 | __ssh_opts__+=${__server_alive_count_max__:+-o ServerAliveCountMax=$__server_alive_count_max__ } 217 | __ssh_opts__+=${__server_alive_interval__:+-o ServerAliveInterval=$__server_alive_interval__ } 218 | __ssh_opts__+=${__stream_local_bind_mask__:+-o StreamLocalBindMask=$__stream_local_bind_mask__ } 219 | __ssh_opts__+=${__stream_local_bind_unlink__:+-o StreamLocalBindUnlink=$__stream_local_bind_unlink__ } 220 | __ssh_opts__+=${__strict_host_key_checking__:+-o StrictHostKeyChecking=$__strict_host_key_checking__ } 221 | __ssh_opts__+=${__tcp_keep_alive__:+-o TCPKeepAlive=$__tcp_keep_alive__ } 222 | __ssh_opts__+=${__tunnel_device__:+-o TunnelDevice=$__tunnel_device__ } 223 | __ssh_opts__+=${__tunnel__:+-o Tunnel=$__tunnel__ } 224 | __ssh_opts__+=${__update_host_keys__:+-o UpdateHostKeys=$__update_host_keys__ } 225 | __ssh_opts__+=${__use_privileged_port__:+-o UsePrivilegedPort=$__use_privileged_port__ } 226 | __ssh_opts__+=${__user_known_hosts_file__:+-o UserKnownHostsFile=$__user_known_hosts_file__ } 227 | __ssh_opts__+=${__verify_host_key_dns__:+-o VerifyHostKeyDNS=$__verify_host_key_dns__ } 228 | __ssh_opts__+=${__visual_host_key__:+-o VisualHostKey=$__visual_host_key__ } 229 | __ssh_opts__+=${__xauth_location__:+-o XAuthLocation=$__xauth_location__ } 230 | 231 | # Gera as chaves de decodificação da senha e salva as configurações 232 | # de conexão no objeto da sessão. 233 | IFS='|' read -r __sha__ __aes__ < <(ssh.__openssl_crypt__ "$__pass__") 234 | printf -v $1 '%s|%s|%s|%s|%s' "$__host__" "$__user__" "$__sha__" "$__aes__" "$__ssh_opts__" 235 | 236 | return $? 237 | } 238 | 239 | # .FUNCTION ssh.shell -> [bool] 240 | # 241 | # Executa os comandos na sessão especificada. 242 | # 243 | # == EXEMPLO == 244 | # 245 | # #!/bin/bash 246 | # 247 | # source ssh.sh 248 | # 249 | # var client ssh_t # Sessão 250 | # var config ssh_config_st # Configurações 251 | # 252 | # # Definindo configurações de conexão e autenticação. 253 | # config.host = '192.168.25.10' 254 | # config.user = 'shaman' 255 | # config.pass = 'senha123' 256 | # 257 | # # Criando uma nova sessão com as configurações estabelecidas. 258 | # client.new config 259 | # 260 | # # Executando comandos no host remoto. 261 | # client.shell 'lsb_release -a; echo ---; who' 262 | # 263 | # == SAÍDA == 264 | # 265 | # Distributor ID: Ubuntu 266 | # Description: Ubuntu 16.04 LTS 267 | # Release: 16.04 268 | # Codename: xenial 269 | # --- 270 | # ubuntu tty7 2019-01-12 11:41 (:0) 271 | # shaman pts/4 2019-01-12 12:00 (192.168.25.3) 272 | # 273 | function ssh.shell() 274 | { 275 | getopt.parse 2 "session:ssh_t:$1" "commands:str:$2" "${@:3}" 276 | 277 | local __ssh_key__ __user__ __host__ __sha__ __aes__ __ssh_opts__ 278 | 279 | IFS='|' read -r __host__ __user__ __sha__ __aes__ __ssh_opts__ <<< ${!1} 280 | __ssh_key__=$(ssh.__openssl_decrypt__ "$__sha__" "$__aes__") 281 | 282 | export DISPLAY=:0 283 | export SSH_ASKPASS=$__ssh_key__ 284 | 285 | # Remove o script de decodificação em caso de falha ou sucesso. 286 | trap "rm -f $__ssh_key__ &>/dev/null; unset SSH_ASKPASS DISPLAY" SIGINT SIGTERM SIGKILL SIGTSTP RETURN 287 | 288 | # Inicia uma nova sessão de conexão com o host remoto. 289 | setsid -w ssh -qt \ 290 | -o StrictHostKeyChecking=no \ 291 | -o UserKnownHostsFile=/dev/null \ 292 | $__ssh_opts__ \ 293 | $__user__@$__host__ \ 294 | "$2" 295 | 296 | # Falha de conexão. 297 | if [[ $? -eq 255 ]]; then 298 | error.error "'$__host__' não foi possível conectar ao host" 299 | return 1 300 | fi 301 | 302 | return $? 303 | } 304 | 305 | # .FUNCTION ssh.exec -> [bool] 306 | # 307 | # Executa o script no host remoto. 308 | # 309 | function ssh.exec() 310 | { 311 | getopt.parse 2 "session:ssh_t:$1" "script:str:$2" "${@:3}" 312 | 313 | local __host__ __user__ __sha__ __aes__ __ssh_opts__ __ssh_key__ 314 | 315 | if [[ ! -f "$2" ]]; then 316 | error.error "'$2' não é um arquivo regular" 317 | return 1 318 | elif [[ ! -r "$2" ]]; then 319 | error.error "'$2' não foi possível ler o arquivo" 320 | return 1 321 | fi 322 | 323 | IFS='|' read -r __host__ __user__ __sha__ __aes__ __ssh_opts__ <<< ${!1} 324 | 325 | __ssh_key__=$(ssh.__openssl_decrypt__ "$__sha__" "$__aes__") 326 | 327 | export DISPLAY=:0 328 | export SSH_ASKPASS=$__ssh_key__ 329 | 330 | trap "rm -f $__ssh_key__ &>/dev/null; unset SSH_ASKPASS DISPLAY" SIGINT SIGTERM SIGKILL SIGTSTP RETURN 331 | 332 | setsid -w ssh -qt \ 333 | -o StrictHostKeyChecking=no \ 334 | -o UserKnownHostsFile=/dev/null \ 335 | $__ssh_opts__ \ 336 | $__user__@$__host__ < "$2" 337 | 338 | if [[ $? -eq 255 ]]; then 339 | error.error "'$__host__' não foi possível conectar ao host" 340 | return 1 341 | fi 342 | 343 | return $? 344 | } 345 | 346 | # .FUNCTION ssh.upload -> [bool] 347 | # 348 | # Envia direetório/arquivo local para o host remoto. 349 | # 350 | function ssh.upload() 351 | { 352 | getopt.parse 3 "session:ssh_t:$1" "srcpath:str:$2" "destpath:str:$3" "${@:4}" 353 | 354 | local __host__ __user__ __sha__ __aes__ __ssh_opts__ __ssh_key__ __rec__ 355 | 356 | if [[ ! -r "$2" ]]; then 357 | error.error "'$2' não foi possível ler o arquivo ou diretório" 358 | return 1 359 | fi 360 | 361 | IFS='|' read -r __host__ __user__ __sha__ __aes__ __ssh_opts__ <<< ${!1} 362 | __ssh_key__=$(ssh.__openssl_decrypt__ "$__sha__" "$__aes__") 363 | 364 | export DISPLAY=:0 365 | export SSH_ASKPASS=$__ssh_key__ 366 | 367 | trap "rm -f $__ssh_key &>/dev/null; unset SSH_ASKPASS DISPLAY" SIGINT SIGTERM SIGKILL SIGTSTP RETURN 368 | 369 | setsid -w scp -q \ 370 | -o StrictHostKeyChecking=no \ 371 | -o UserKnownHostsFile=/dev/null \ 372 | $__ssh_opts__ \ 373 | -r "$2" \ 374 | $__user__@$__host__:"$3" 375 | 376 | return $? 377 | } 378 | 379 | # .FUNCTION ssh.download -> [bool] 380 | # 381 | # Baixa o diretório/arquivo remoto para o destino especificado. 382 | # 383 | function ssh.download() 384 | { 385 | getopt.parse 3 "session:ssh_t:$1" "srcpath:str:$2" "destpath:str:$3" "${@:4}" 386 | 387 | local __host__ __user__ __sha__ __aes__ __ssh_opts__ __ssh_key__ 388 | 389 | IFS='|' read -r __host__ __user__ __sha__ __aes__ __ssh_opts__ <<< ${!1} 390 | __ssh_key__=$(ssh.__openssl_decrypt__ "$__sha__" "$__aes__") 391 | 392 | export DISPLAY=:0 393 | export SSH_ASKPASS=$__ssh_key__ 394 | 395 | trap "rm -f $__ssh_key &>/dev/null; unset SSH_ASKPASS DISPLAY" SIGINT SIGTERM SIGKILL SIGTSTP RETURN 396 | 397 | setsid -w scp -q \ 398 | -o StrictHostKeyChecking=no \ 399 | -o UserKnownHostsFile=/dev/null \ 400 | $__ssh_opts__ \ 401 | -r $__user__@$__host__:"$2" \ 402 | "$3" 403 | 404 | return $? 405 | } 406 | 407 | # .FUNCTION ssh.close -> [bool] 408 | # 409 | # Finaliza a sessão. 410 | # 411 | function ssh.close() 412 | { 413 | getopt.parse 1 "session:ssh_t:$1" "${@:2}" 414 | del $1 415 | return $? 416 | } 417 | 418 | function ssh.__openssl_crypt__() 419 | { 420 | local sha aes seed 421 | 422 | printf -v seed '%(%s)T' 423 | IFS=' ' read sha _ < <(sha256sum <<< "$((RANDOM * (BASHPID ^ seed)))") 424 | aes=$(openssl enc -e -aes-256-cbc -a -k "$sha" <<< $1) 425 | printf '%s|%s\n' "$sha" "$aes" 426 | 427 | return $? 428 | } 429 | 430 | function ssh.__openssl_decrypt__() 431 | { 432 | local ssh_key 433 | 434 | # Gera o script de decodificação da chave ssh. 435 | ssh_key=$(mktemp -qtu XXXXXXXXXXXXXXXXXXXX.key) 436 | cat > $ssh_key << _eof 437 | #!/bin/bash 438 | openssl enc -d -aes-256-cbc -k "$1" < <(openssl base64 -d <<< "$2") 439 | _eof 440 | 441 | chmod u+x $ssh_key 442 | echo "$ssh_key" # Retorna o script. 443 | 444 | return $? 445 | } 446 | 447 | # Estrutura 448 | var ssh_config_st struct_t 449 | 450 | # .STRUCT ssh_config_st 451 | # 452 | # Implementa o objeto 'S" com os membros: 453 | # 454 | # S.user [str] 455 | # S.pass [str] 456 | # S.host [str] 457 | # S.port [uint] 458 | # S.forward_auth [bool] 459 | # S.bind_address [str] 460 | # S.log_file [str] 461 | # S.config_file [str] 462 | # S.add_keys_to_agent [str] 463 | # S.address_family [str] 464 | # S.batch_mode [str] 465 | # S.canonical_domains [str] 466 | # S.canonicalize_fallback_local [str] 467 | # S.canonicalize_hostname [str] 468 | # S.canonicalize_max_dots [uint] 469 | # S.canonicalize_permitted_cnames [str] 470 | # S.cetificate_file [str] 471 | # S.challenge_response_authentication [str] 472 | # S.check_host_ip [str] 473 | # S.ciphers [str] 474 | # S.clear_all_forwardings [str] 475 | # S.escape_char [str] 476 | # S.compression [str] 477 | # S.compression_level [uint] 478 | # S.connection_attempts [uint] 479 | # S.connect_timeout [uint] 480 | # S.control_master [str] 481 | # S.control_path [str] 482 | # S.control_persist [str] 483 | # S.dynamic_forward [str] 484 | # S.enable_ssh_keysing [str] 485 | # S.exit_on_forward_failure [str] 486 | # S.finger_print_hash [str] 487 | # S.forward_agent [str] 488 | # S.forward_x11 [str] 489 | # S.forward_x11_timeout [uint] 490 | # S.forward_x11_trusted [str] 491 | # S.gateway_ports [str] 492 | # S.global_known_hosts_file [str] 493 | # S.gssapi_authentication [str] 494 | # S.gssapi_key_exchange [str] 495 | # S.gssapi_client_identity [str] 496 | # S.gssapi_server_identity [str] 497 | # S.gssapi_delegate_credentials [str] 498 | # S.gssapi_renewal_forces_rekey [str] 499 | # S.gssapi_trust_dns [str] 500 | # S.hash_known_hosts [str] 501 | # S.host_based_authentication [str] 502 | # S.host_based_key_types [str] 503 | # S.host_key_algorithms [str] 504 | # S.host_key_alias [str] 505 | # S.hostname [str] 506 | # S.identities_only [str] 507 | # S.identity_agent [str] 508 | # S.identity_file [str] 509 | # S.ignore_unknown [str] 510 | # S.include [str] 511 | # S.ip_qos [str] 512 | # S.kbd_interactive_authentication [str] 513 | # S.kbd_interactive_devices [str] 514 | # S.kex_algorithms [str] 515 | # S.local_command [str] 516 | # S.local_forward [str] 517 | # S.log_level [str] 518 | # S.macs [str] 519 | # S.no_host_authentication_for_local_host [str] 520 | # S.number_of_password_prompts [uint] 521 | # S.password_authentication [str] 522 | # S.permit_local_command [str] 523 | # S.pkcs11_provider [str] 524 | # S.preferred_authentications [str] 525 | # S.proxy_command [str] 526 | # S.proxy_jump [str] 527 | # S.proxy_use_fdpass [str] 528 | # S.pubkey_accepted_key_types [str] 529 | # S.pubkey_authentication [str] 530 | # S.rekey_limit [str] 531 | # S.remote_forward [str] 532 | # S.remote_command [str] 533 | # S.request_tty [str] 534 | # S.revoked_host_keys [str] 535 | # S.send_env [str] 536 | # S.server_alive_count_max [uint] 537 | # S.server_alive_interval [uint] 538 | # S.stream_local_bind_mask [str] 539 | # S.stream_local_bind_unlink [str] 540 | # S.strict_host_key_checking [str] 541 | # S.tcp_keep_alive [str] 542 | # S.tunnel [str] 543 | # S.tunnel_device [str] 544 | # S.update_host_keys [str] 545 | # S.use_privileged_port [str] 546 | # S.user_known_hosts_file [str] 547 | # S.verify_host_key_dns [str] 548 | # S.visual_host_key [str] 549 | # S.xauth_location [str] 550 | # 551 | ssh_config_st.__add__ user str \ 552 | pass str \ 553 | host str \ 554 | port uint \ 555 | forward_auth bool \ 556 | bind_address str \ 557 | log_file str \ 558 | config_file str \ 559 | add_keys_to_agent str \ 560 | address_family str \ 561 | batch_mode str \ 562 | canonical_domains str \ 563 | canonicalize_fallback_local str \ 564 | canonicalize_hostname str \ 565 | canonicalize_max_dots uint \ 566 | canonicalize_permitted_cnames str \ 567 | cetificate_file str \ 568 | challenge_response_authentication str \ 569 | check_host_ip str \ 570 | ciphers str \ 571 | clear_all_forwardings str \ 572 | escape_char str \ 573 | compression str \ 574 | compression_level uint \ 575 | connection_attempts uint \ 576 | connect_timeout uint \ 577 | control_master str \ 578 | control_path str \ 579 | control_persist str \ 580 | dynamic_forward str \ 581 | enable_ssh_keysing str \ 582 | exit_on_forward_failure str \ 583 | finger_print_hash str \ 584 | forward_agent str \ 585 | forward_x11 str \ 586 | forward_x11_timeout uint \ 587 | forward_x11_trusted str \ 588 | gateway_ports str \ 589 | global_known_hosts_file str \ 590 | gssapi_authentication str \ 591 | gssapi_key_exchange str \ 592 | gssapi_client_identity str \ 593 | gssapi_server_identity str \ 594 | gssapi_delegate_credentials str \ 595 | gssapi_renewal_forces_rekey str \ 596 | gssapi_trust_dns str \ 597 | hash_known_hosts str \ 598 | host_based_authentication str \ 599 | host_based_key_types str \ 600 | host_key_algorithms str \ 601 | host_key_alias str \ 602 | hostname str \ 603 | identities_only str \ 604 | identity_agent str \ 605 | identity_file str \ 606 | ignore_unknown str \ 607 | include str \ 608 | ip_qos str \ 609 | kbd_interactive_authentication str \ 610 | kbd_interactive_devices str \ 611 | kex_algorithms str \ 612 | local_command str \ 613 | local_forward str \ 614 | log_level str \ 615 | macs str \ 616 | no_host_authentication_for_local_host str \ 617 | number_of_password_prompts uint \ 618 | password_authentication str \ 619 | permit_local_command str \ 620 | pkcs11_provider str \ 621 | preferred_authentications str \ 622 | proxy_command str \ 623 | proxy_jump str \ 624 | proxy_use_fdpass str \ 625 | pubkey_accepted_key_types str \ 626 | pubkey_authentication str \ 627 | rekey_limit str \ 628 | remote_forward str \ 629 | remote_command str \ 630 | request_tty str \ 631 | revoked_host_keys str \ 632 | send_env str \ 633 | server_alive_count_max uint \ 634 | server_alive_interval uint \ 635 | stream_local_bind_mask str \ 636 | stream_local_bind_unlink str \ 637 | strict_host_key_checking str \ 638 | tcp_keep_alive str \ 639 | tunnel str \ 640 | tunnel_device str \ 641 | update_host_keys str \ 642 | use_privileged_port str \ 643 | user_known_hosts_file str \ 644 | verify_host_key_dns str \ 645 | visual_host_key str \ 646 | xauth_location str 647 | 648 | # .TYPE ssh_t 649 | # 650 | # Implementa o objeto 'S' com os métodos: 651 | # 652 | # S.new 653 | # S.shell 654 | # S.exec 655 | # S.upload 656 | # S.download 657 | # S.close 658 | # 659 | typedef ssh_t ssh.new \ 660 | ssh.shell \ 661 | ssh.exec \ 662 | ssh.upload \ 663 | ssh.download \ 664 | ssh.close 665 | 666 | # Funções (somente-leitura) 667 | readonly -f ssh.new \ 668 | ssh.shell \ 669 | ssh.exec \ 670 | ssh.upload \ 671 | ssh.download \ 672 | ssh.close \ 673 | ssh.__openssl_decrypt__ \ 674 | ssh.__openssl_crypt__ 675 | 676 | # /* __SSH_SH__ */ 677 | -------------------------------------------------------------------------------- /src/string.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __STRING_SH__ ] && return 0 21 | 22 | readonly __STRING_SH__=1 23 | 24 | source builtin.sh 25 | 26 | # .FUNCTION string.len -> [uint]|[bool] 27 | # 28 | # Retorna o comprimento de 'expr'. 29 | # 30 | function string.len() 31 | { 32 | getopt.parse 1 "expr:str:$1" "${@:2}" 33 | 34 | echo ${#1} 35 | return $? 36 | } 37 | 38 | # .FUNCTION string.capitalize -> [str]|[bool] 39 | # 40 | # Retorna uma cópia em letras maiúsculas de 'expr', ou seja, torna o primeiro 41 | # caractere em maiúsculo e o restante em minúsculos. 42 | # 43 | function string.capitalize() 44 | { 45 | getopt.parse 1 "expr:str:$1" "${@:2}" 46 | 47 | local sub=${1:1} 48 | local ini=${1:0:1} 49 | echo "${ini^}${sub,,}" 50 | return $? 51 | } 52 | 53 | # .FUNCTION string.center <[uint]width> -> [str]|[bool] 54 | # 55 | # Retorna uma cópia de 'expr' centralizando o texto. 56 | # 57 | function string.center() 58 | { 59 | getopt.parse 3 "expr:str:$1" "fillchar:char:$2" "width:uint:$3" "${@:4}" 60 | 61 | local ch cr 62 | local lc=$(($3-${#1})) 63 | 64 | ((lc > 0)) && printf -v ch '%*s' $((lc/2)) 65 | (((lc % 2) == 1)) && cr=$2 66 | ch=${ch// /$2} 67 | echo "${ch}${1}${ch}${cr}" 68 | 69 | return $? 70 | } 71 | 72 | # .FUNCTION string.count -> [uint]|[bool] 73 | # 74 | # Retorna 'N' ocorrências de 'sub' em 'expr'. 75 | # 76 | function string.count() 77 | { 78 | getopt.parse 2 "expr:str:$1" "sub:str:$2" "${@:3}" 79 | 80 | local expr=$1 81 | local c 82 | 83 | while [[ $2 && $expr =~ $2 ]]; do 84 | ((c++)); expr=${expr/$BASH_REMATCH/} 85 | done 86 | 87 | echo ${c:-0} 88 | 89 | return $? 90 | } 91 | 92 | # .FUNCTION string.endswith -> [bool] 93 | # 94 | # Retorna 'true' se 'expr' termina com 'suffix, caso contrário 'false'. 95 | # 96 | function string.endswith() 97 | { 98 | getopt.parse 2 "expr:str:$1" "suffix:str:$2" "${@:3}" 99 | 100 | [[ $1 =~ $2$ ]] 101 | return $? 102 | } 103 | 104 | # .FUNCTION string.startswith -> [bool] 105 | # 106 | # Retorna 'true' se 'expr' inicia com 'prefix', caso contrário 'false'. 107 | # 108 | function string.startswith() 109 | { 110 | getopt.parse 2 "expr:str:$1" "prefix:str:$2" "${@:3}" 111 | 112 | [[ $1 =~ ^$2 ]] 113 | return $? 114 | } 115 | 116 | # .FUNCTION string.expandspace -> [bool] 117 | # 118 | # Retorna uma sequência caracteres em que os espaços são expandidos 119 | # ao comprimento especificado em 'size'. 120 | # 121 | function string.expandspaces() 122 | { 123 | getopt.parse 2 "expr:str:$1" "size:str:$2" "${@:3}" 124 | 125 | local spc 126 | printf -v spc '%*s' $2 127 | echo "${1// /$spc}" 128 | return $? 129 | } 130 | 131 | # .FUNCTION string.find -> [int]|[bool] 132 | # 133 | # Retorna o índice mais baixo da ocorrência de 'sub' em 'expr'. 134 | # Se não houver correspondência é retornado '-1'. 135 | # 136 | function string.find() 137 | { 138 | getopt.parse 2 "expr:str:$1" "sub:str:$2" "${@:3}" 139 | 140 | local pos sub 141 | 142 | sub=${1#*$2} 143 | pos=$((${#1}-${#sub}-${#2})) 144 | ((pos < 0)) && pos=-1 145 | echo $pos 146 | return $? 147 | } 148 | 149 | # .FUNCTION string.rfind -> [int]|[bool] 150 | # 151 | # Retorna o índice mais alto da ocorrência de 'sub' em 'expr'. 152 | # Se não houver correspondência é retornado '-1'. 153 | # 154 | function string.rfind() 155 | { 156 | getopt.parse 2 "expr:str:$1" "sub:str:$2" "${@:3}" 157 | 158 | local pos sub 159 | 160 | sub=${1##*$2} 161 | pos=$((${#1}-${#sub}-${#2})) 162 | ((pos < 0)) && pos=-1 163 | echo $pos 164 | return $? 165 | } 166 | 167 | # .FUNCTION string.isalnum -> [bool] 168 | # 169 | # Retorna 'true' se 'expr' contém letras e dígitos. 170 | # 171 | function string.isalnum() 172 | { 173 | getopt.parse 1 "expr:str:$1" "${@:2}" 174 | 175 | [[ $1 == +([[:alnum:]]) ]] 176 | return $? 177 | } 178 | 179 | # .FUNCTION string.isalpha -> [bool] 180 | # 181 | # retorna 'true' se 'expr' contém somente letras. 182 | # 183 | function string.isalpha() 184 | { 185 | getopt.parse 1 "expr:str:$1" "${@:2}" 186 | 187 | [[ $1 == +([[:alpha:]]) ]] 188 | return $? 189 | } 190 | 191 | # .FUNCTION string.isdigit -> [bool] 192 | # 193 | # Retorna 'true' se 'expr' contém somente dígitos. 194 | # 195 | function string.isdigit() 196 | { 197 | getopt.parse 1 "expr:str:$1" "${@:2}" 198 | 199 | [[ $1 == +([[:digit:]]) ]] 200 | return $? 201 | } 202 | 203 | # .FUNCTION string.isspace -> [bool] 204 | # 205 | # Retorna 'true' se 'expr' contém somente espaços. 206 | # 207 | function string.isspace() 208 | { 209 | getopt.parse 1 "expr:str:$1" "${@:2}" 210 | 211 | [[ $1 == +([[:space:]]) ]] 212 | return $? 213 | } 214 | 215 | # .FUNCTION string.isprint -> [bool] 216 | # 217 | # Retorna 'true' se 'expr' contém somente caracteres imprimíveis. 218 | # 219 | function string.isprint() 220 | { 221 | getopt.parse 1 "expr:str:$1" "${@:2}" 222 | 223 | [[ $1 == +([[:print:]]) ]] 224 | return $? 225 | } 226 | 227 | 228 | # .FUNCTION string.islower -> [bool] 229 | # 230 | # Retorna 'true' se 'expr' contém somente caracteres minúsculos. 231 | # 232 | function string.islower() 233 | { 234 | getopt.parse 1 "expr:str:$1" "${@:2}" 235 | 236 | [[ $1 == +([^[:upper:]]) ]] 237 | return $? 238 | } 239 | 240 | # .FUNCTION string.isupper -> [bool] 241 | # 242 | # Retorna 'true' se 'expr' contém somente caracteres maiúsculos. 243 | # 244 | function string.isupper() 245 | { 246 | getopt.parse 1 "expr:str:$1" "${@:2}" 247 | 248 | [[ $1 == +([^[:lower:]]) ]] 249 | return $? 250 | } 251 | 252 | # .FUNCTION string.istitle -> [bool] 253 | # 254 | # Retorna 'true' se 'expr' é uma string de titulo e há pelo menos um 255 | # caractere em maiúsculo, ou seja, caracteres maiúsculos só podem seguir sem 256 | # caracteres e caracteres minúsculos apenas os maiúsculos. Retorna 'false' 257 | # de outra forma. 258 | # 259 | function string.istitle() 260 | { 261 | getopt.parse 1 "expr:str:$1" "${@:2}" 262 | 263 | [[ $1 == +(*([^[:alpha:]])@([[:upper:]])+([[:lower:]])) ]] 264 | return $? 265 | } 266 | 267 | # .FUNCTION string.join [str]|[bool] 268 | # 269 | # Retorna uma string que é concatenação dos elementos iteráveis delimitados por 'sep'. 270 | # 271 | function string.join() 272 | { 273 | getopt.parse 2 "expr:str:$1" "sep:str:$2" "${@:3}" 274 | 275 | local iter 276 | 277 | mapfile -t iter <<< "$1" 278 | printf -v expr "%s${2//%/%%}" "${iter[@]}" 279 | echo "${expr%$2}" 280 | return $? 281 | } 282 | 283 | # .FUNCTION string.ljust -> [str]|[bool] 284 | # 285 | # Retorna uma string justificada à esquerda em uma cadeia de largura de comprimento. 286 | # O preenchimento é feito usando o caractere de preenchimento especificado em 'fillchar'. 287 | # 288 | function string.ljust() 289 | { 290 | getopt.parse 3 "expr:str:$1" "fillchar:char:$2" "width:uint:$3" "${@:4}" 291 | 292 | local ch wd 293 | wd=$(($3-${#1})) 294 | printf -v ch '%*s' $(($wd > 0 ? $wd : 0)) 295 | echo "${ch// /$2}${1}" 296 | return $? 297 | } 298 | 299 | # .FUNCTION string.rjust -> [str]|[bool] 300 | # 301 | # Retorna uma string justificada à diretia em uma cadeia de largura de comprimento. 302 | # O preenchimento é feito usando o caractere de preenchimento especificado em 'fillchar'. 303 | # 304 | function string.rjust() 305 | { 306 | getopt.parse 3 "expr:str:$1" "fillchar:char:$2" "width:uint:$3" "${@:4}" 307 | 308 | local ch wd 309 | wd=$(($3-${#1})) 310 | printf -v ch '%*s' $(($wd > 0 ? $wd : 0)) 311 | echo "${1}${ch// /$2}" 312 | return $? 313 | } 314 | 315 | # .FUNCTION string.lower -> [str]|[bool] 316 | # 317 | # Converte a cadeia de caracteres para minúsculo. 318 | # 319 | function string.lower() 320 | { 321 | getopt.parse 1 "expr:str:$1" "${@:2}" 322 | 323 | echo "${1,,}" 324 | return $? 325 | } 326 | 327 | # .FUNCTION string.upper -> [str]|[bool] 328 | # 329 | # Converte a cadeia de caracteres para maiúsculo. 330 | # 331 | function string.upper() 332 | { 333 | getopt.parse 1 "expr:str:$1" "${@:2}" 334 | 335 | echo "${1^^}" 336 | return $? 337 | } 338 | 339 | # .FUNCTION string.strip -> [str]|[bool] 340 | # 341 | # Retorna uma cópia da string removendo a substring do inicio e final 342 | # da cadeia de caracteres. 343 | # 344 | function string.strip() 345 | { 346 | getopt.parse 2 "expr:str:$1" "sub:str:$2" "${@:3}" 347 | 348 | local on expr 349 | 350 | shopt -q extglob && on='s' 351 | shopt -s extglob 352 | expr=${1##+($2)} 353 | echo "${expr%%+($2)}" 354 | shopt -${on:-u} extglob 355 | return $? 356 | } 357 | 358 | # .FUNCTION string.lstrip -> [str]|[bool] 359 | # 360 | # Retorna uma cópia da string removendo a substring do inicio da 361 | # cadeia de caracteres. 362 | # 363 | function string.lstrip() 364 | { 365 | getopt.parse 2 "expr:str:$1" "sub:str:$2" "${@:3}" 366 | 367 | local on 368 | 369 | shopt -q extglob && on='s' 370 | shopt -s extglob 371 | echo "${1##+($2)}" 372 | shopt -${on:-u} extglob 373 | return $? 374 | } 375 | 376 | # .FUNCTION string.rstrip -> [str]|[bool] 377 | # 378 | # Retorna uma cópia da string removendo a substring do final da 379 | # cadeia de caracteres. 380 | # 381 | function string.rstrip() 382 | { 383 | getopt.parse 2 "expr:str:$1" "sub:str:$2" "${@:3}" 384 | 385 | local on 386 | 387 | shopt -q extglob && on='s' 388 | shopt -s extglob 389 | echo "${1%%+($2)}" 390 | shopt -${on:-u} extglob 391 | return $? 392 | } 393 | 394 | # .FUNCTION string.replace -> [str]|[bool] 395 | # 396 | # Retorna uma cópia da string substituindo 'N' ocorrências de 'old' por 'new'. 397 | # 398 | function string.replace 399 | { 400 | getopt.parse 4 "expr:str:$1" "old:str:$2" "new:str:$3" "count:int:$4" "${@:5}" 401 | 402 | local expr c i 403 | 404 | expr=$1 405 | 406 | for ((i=0; i < ${#expr}; i++)); do 407 | if [[ ${expr:$i:${#2}} == $2 ]]; then 408 | expr=${expr:0:$i}${3}${expr:$(($i+${#2}))} 409 | i=$(($i+${#3})) 410 | [[ $((++c)) -eq $4 ]] && break 411 | fi 412 | done 413 | 414 | echo "$expr" 415 | 416 | return $? 417 | } 418 | 419 | # .FUNCTION string.fnreplace ... -> [str]|[bool] 420 | # 421 | # Retorna uma cópia da string substituindo 'N' ocorrências de 'old' pelo retorno da função. 422 | # A função é chamada a cada ocorrência, passando como argumento posicional '$1' a expressão 423 | # casada com 'N' args (opcional). 424 | # 425 | # == EXEMPLO == 426 | # 427 | # source string.sh 428 | # 429 | # texto='Linux é vida, Linux é liberdade, Linux é tudo!!' 430 | # 431 | # # Função que remove os dois primeiros caracteres da expressão. 432 | # rm_chars(){ 433 | # echo "${1#??}" 434 | # } 435 | # 436 | # # Manipulando a palavra 'Linux' utilizando funções já existentes. 437 | # string.fnreplace "$texto" 'Linux' -1 string.reverse 438 | # string.fnreplace "$texto" 'Linux ' -1 string.repeat 3 439 | # string.fnreplace "$texto" 'Linux' 2 string.upper 440 | # 441 | # # Função personalizada. 442 | # string.fnreplace "$texto" 'Linux' -1 rm_chars 443 | # 444 | # == SAÍDA == 445 | # 446 | # xuniL é vida, xuniL é liberdade, xuniL é tudo!! 447 | # Linux Linux Linux é vida, Linux Linux Linux é liberdade, Linux Linux Linux é tudo!! 448 | # LINUX é vida, LINUX é liberdade, Linux é tudo!! 449 | # nux é vida, nux é liberdade, nux é tudo!! 450 | # 451 | function string.fnreplace 452 | { 453 | getopt.parse -1 "expr:str:$1" "old:str:$2" "count:int:$3" "func:function:$4" "args:str:$5" ... "${@:6}" 454 | 455 | local expr fn i c 456 | 457 | expr=$1 458 | 459 | for ((i=0; i < ${#expr}; i++)); do 460 | if [[ ${expr:$i:${#2}} == $2 ]]; then 461 | fn=$($4 "$2" "${@:5}") 462 | expr=${expr:0:$i}${fn}${expr:$(($i+${#2}))} 463 | i=$(($i+${#fn})) 464 | [[ $((++c)) -eq $3 ]] && break 465 | fi 466 | done 467 | 468 | echo "$expr" 469 | 470 | return $? 471 | } 472 | 473 | # .FUNCTION string.replacers ... -> [str]|[bool] 474 | # 475 | # Retorna uma cópia da string substituindo todas as ocorrências de 'old' por 'new', 476 | # podendo ser especificado mais de um conjunto de substituição. 477 | # 478 | # == EXEMPLO == 479 | # 480 | # source string.sh 481 | # 482 | # texto='A Microsoft além do Windows agora tem sua própria distro Linux'. 483 | # 484 | # # Substituições. 485 | # string.replacers "$texto" 'Windows' 'Ruindows' 'Microsoft' 'Micro$oft' 'Linux' 'Rindux' 486 | # 487 | # == SAÍDA == 488 | # 489 | # A Micro$oft além do Ruindows agora tem sua própria distro Rindux. 490 | # 491 | function string.replacers() 492 | { 493 | getopt.parse -1 "expr:str:$1" "old:str:$2" "new:str:$3" ... "${@:4}" 494 | 495 | local expr=$1 496 | 497 | set "${@:2}" 498 | 499 | while [[ $1 && $expr == *$1* ]]; do 500 | expr=${expr//$1/$2} 501 | shift 2 502 | done 503 | 504 | echo "$expr" 505 | 506 | return $? 507 | } 508 | 509 | # .FUNCTION string.split -> [str]|[bool] 510 | # 511 | # Retorna uma lista de palavras delimitadas por 'sep' em 'count' vezes. 512 | # Se 'count' for menor que zero aplica a ação em todas as ocorrências. 513 | # 514 | # == EXEMPLO == 515 | # 516 | # source string.sh 517 | # 518 | # distros='Slackware,Debian,Centos,Ubuntu,Manjaro' 519 | # 520 | # string.split "$distro" ',' -1 521 | # echo --- 522 | # string.split "$distro" ',' 2 523 | # 524 | # == SAÍDA == 525 | # 526 | # Slackware 527 | # Debian 528 | # Centos 529 | # Ubuntu 530 | # Manjaro 531 | # --- 532 | # Slackware 533 | # Debian 534 | # Centos,Ubuntu,Manjaro 535 | # 536 | function string.split() 537 | { 538 | getopt.parse 3 "expr:str:$1" "sep:str:$2" "count:int:$3" "${@:4}" 539 | 540 | local c expr=$1 541 | 542 | while [[ $expr == *$2* ]]; do 543 | [[ $((c++)) -eq $3 ]] && break 544 | expr=${expr/$2/$'\n'} 545 | done 546 | 547 | mapfile -t expr <<< "$expr" 548 | printf '%s\n' "${expr[@]}" 549 | 550 | return $? 551 | } 552 | 553 | # .FUNCTION string.swapcase -> [str]|[bool] 554 | # 555 | # Retorna uma cópia de 'expr' convertendo os caracteres minúsculos para 556 | # maiúsculos e vice-versa. 557 | # 558 | function string.swapcase() 559 | { 560 | getopt.parse 1 "expr:str:$1" "${@:2}" 561 | 562 | echo "${1~~}" 563 | return $? 564 | } 565 | 566 | # .FUNCTION string.title -> [str]|[bool] 567 | # 568 | # Retorna uma cópia titulada de 'expr', ou seja, as palavras começam com 569 | # a primeira letra maiúscula e as demais minúsculas. 570 | # 571 | function string.title() 572 | { 573 | getopt.parse 1 "expr:str:$1" "${@:2}" 574 | 575 | local expr=${1,,} 576 | 577 | while [[ $expr =~ [^a-zA-Z][a-z] ]]; do 578 | expr=${expr/$BASH_REMATCH/${BASH_REMATCH^^}} 579 | done 580 | 581 | echo "${expr^}" 582 | 583 | return $? 584 | } 585 | 586 | # .FUNCTION string.reverse -> [str]|[bool] 587 | # 588 | # Retorna uma cópia invertida da sequẽncia de caracteres de 'expr'. 589 | # 590 | function string.reverse() 591 | { 592 | getopt.parse 1 "expr:str:$1" "${@:2}" 593 | 594 | rev <<< "$1" 595 | return $? 596 | } 597 | 598 | # .FUNCTION string.repeat -> [str]|[bool] 599 | # 600 | # Retorna uma copia de 'expr' repetida 'N' vezes. 601 | # 602 | function string.repeat() 603 | { 604 | getopt.parse 2 "expr:str:$1" "count:uint:$2" "${@:3}" 605 | 606 | local i 607 | for ((i=0; i < $2; i++)); do 608 | echo -n "$1" 609 | done; echo 610 | return $? 611 | } 612 | 613 | # .FUNCTION string.zfill -> [str]|[bool] 614 | # 615 | # Preenche a expressão com 'N' zeros a esquerda. 616 | # 617 | function string.zfill() 618 | { 619 | getopt.parse 2 "expr:str:$1" "witdh:uint:$2" "${@:3}" 620 | 621 | local i 622 | for ((i=0; i < $2; i++)); do 623 | echo -n '0' 624 | done; echo "$1" 625 | return $? 626 | } 627 | 628 | # .FUNCTION string.compare -> [bool] 629 | # 630 | # Compara as expressões e retorna 'true' se forem iguais, caso contrário 'false'. 631 | # Se 'case' for igual a 'true' ativa a análise tipográfica de diferenciação entre 632 | # caracteres maiúsculos e minúsculos. 633 | # 634 | # == EXEMPLO == 635 | # 636 | # source string.sh 637 | # 638 | # string.compare 'Linux' 'LINUX' true && echo true || echo false 639 | # string.compare 'Linux' 'LINUX' false && echo true || echo false 640 | # 641 | # == SAÍDA == 642 | # 643 | # false 644 | # true 645 | # 646 | function string.compare() 647 | { 648 | getopt.parse 3 "expr1:str:$1" "expr2:str:$2" "case:bool:$3" "${@:4}" 649 | 650 | if $3; then [ "$1" == "$2" ]; else [ "${1,,}" == "${2,,}" ]; fi 651 | return $? 652 | } 653 | 654 | # .FUNCTION string.contains -> [bool] 655 | # 656 | # Retorna 'true' se a expressão contém a substring. 657 | # 658 | function string.contains() 659 | { 660 | getopt.parse 2 "expr:str:$1" "sub:str:$2" "${@:3}" 661 | 662 | [[ $1 == *$2* ]] 663 | return $? 664 | } 665 | 666 | # .FUNCTION string.map -> [char]|[uint] 667 | # 668 | # Retorna uma lista iterável da cadeia de caracteres. 669 | # 670 | function string.map() 671 | { 672 | getopt.parse 1 "expr:str:$1" "${@:2}" 673 | 674 | local i 675 | 676 | for ((i=0; i < ${#1}; i++)); do 677 | echo "${1:$i:1}" 678 | done 679 | 680 | return $? 681 | } 682 | 683 | # .FUNCTION string.fnsmap ... -> [str]|[bool] 684 | # 685 | # Aplica a função em cada substring delimitada pelo caractere ' ' espaço contido na expressão 686 | # substituindo-a pelo retorno da função. 687 | # 688 | # == EXEMPLO == 689 | # 690 | # source string.sh 691 | # 692 | # texto='Linux é sinônimo de liberdade e ser livre é uma questão de escolha.' 693 | # 694 | # flag(){ 695 | # echo "[$1]" 696 | # } 697 | # 698 | # char(){ 699 | # echo "[${1:0:1}]" 700 | # } 701 | # 702 | # string.fnsmap "$texto" flag 703 | # string.fnsmap "$texto" char 704 | # string.fnsmap "$texto" string.repeat 2 705 | # 706 | # == SAÍDA == 707 | # 708 | # [Linux] [é] [sinônimo] [de] [liberdade] [e] [ser] [livre] [é] [uma] [questão] [de] [escolha.] 709 | # [L] [é] [s] [d] [l] [e] [s] [l] [é] [u] [q] [d] [e] 710 | # LinuxLinux éé sinônimosinônimo dede liberdadeliberdade ee serser livrelivre éé umauma questãoquestão dede escolha.escolha. 711 | # 712 | function string.fnsmap() 713 | { 714 | getopt.parse -1 "expr:str:$1" "func:function:$2" "args:str:$3" ... "${@:4}" 715 | 716 | local expr str 717 | 718 | while IFS=$'\n' read -r str; do 719 | expr+=$($2 "$str" "${@:3}")' ' 720 | done < <(printf '%s\n' $1) 721 | 722 | echo "$expr" 723 | 724 | return $? 725 | } 726 | 727 | # .FUNCTION string.fncmap ... -> [str]|[bool] 728 | # 729 | # Aplica a função em cada caractere da expressão substituindo-o pelo retorno da função. 730 | # 731 | # == EXEMPLO == 732 | # 733 | # source string.sh 734 | # 735 | # texto='Viva o Linux' 736 | # 737 | # func(){ 738 | # echo "($1)" 739 | # } 740 | # 741 | # string.fncmap "$texto" func 742 | # 743 | # == SAÍDA == 744 | # 745 | # (V)(i)(v)(a)( )(o)( )(L)(i)(n)(u)(x) 746 | # 747 | function string.fncmap() 748 | { 749 | getopt.parse -1 "expr:str:$1" "func:function:$2" "args:str:$3" ... "${@:4}" 750 | 751 | local expr i 752 | 753 | for ((i=0; i < ${#1}; i++)); do 754 | expr+=$($2 "${1:$i:1}" "${@:3}") 755 | done 756 | 757 | echo "$expr" 758 | 759 | return $? 760 | } 761 | 762 | # .FUNCTION string.filter ... -> [str]|[bool] 763 | # 764 | # Filtra a expressão retornando apenas a cadeia de caracteres representada 765 | # pela classe. Pode ser especificada mais de uma classe. 766 | # 767 | # Classes suportadas: 768 | # 769 | # [:alnum:] - todas as letras e dígitos 770 | # [:alpha:] - todas as letras 771 | # [:blank:] - todos os espaços brancos na horizontal 772 | # [:cntrl:] - todos os caracteres de controle 773 | # [:digit:] - todos os dígitos 774 | # [:graph:] - todos os caracteres exibíveis, exceto espaços 775 | # [:lower:] - todas as letras minúsculas 776 | # [:print:] - todos os caracteres exibíveis, inclusive espaços 777 | # [:punct:] - todos os caracteres de pontuação 778 | # [:space:] - todos os espaços brancos na horizontal ou vertical 779 | # [:upper:] - todas as letras maiúsculas 780 | # [:xdigit:] - todos os dígitos hexadecimais 781 | # 782 | # == EXEMPLO == 783 | # 784 | # source string.sh 785 | # 786 | # distro='Ubuntu 16.04, Debian 9, Slackware 14' 787 | # 788 | # string.filter "$distro" [:alpha:] [:space:] 789 | # string.filter "$distro" [:digit:] 790 | # string.filter "$distro" [:punct:] 791 | # 792 | # == SAÍDA == 793 | # 794 | # Ubuntu Debian Slackware 795 | # 1604914 796 | # .,, 797 | # 798 | function string.filter() 799 | { 800 | getopt.parse -1 "expr:str:$1" "class:str:$2" ... "${@:3}" 801 | 802 | echo "${1//[^${@:2}]/}" 803 | return $? 804 | } 805 | 806 | # .FUNCTION string.field ... [str]|[bool] 807 | # 808 | # Retorna 'N' campos delimitados pela substring. Utilize notação negativa para 809 | # captura reversa dos campos, ou seja, se o índice for igual à '-1' é retornado 810 | # o útlimo elemento, '-2' o penúltimo e assim por diante. 811 | # 812 | # == EXEMPLO == 813 | # 814 | # source string.sh 815 | # 816 | # lista='item1,item2,item3,item4,item5' 817 | # 818 | # string.field "$lista" ',' {1..3} 819 | # string.field "$lista" ',' 1 4 820 | # string.field "$lista" ',' {2..5} 821 | # string.field "$lista" ',' -1 822 | # string.field "$lista" 'item3' 1 823 | # 824 | # == SAÍDA == 825 | # 826 | # item1 item2 item3 827 | # item1 item4 828 | # item2 item3 item4 item5 829 | # item5 830 | # item1,item2, 831 | # 832 | function string.field() 833 | { 834 | getopt.parse -1 "expr:str:$1" "sep:str:$2" "field:int:$3" ... "${@:4}" 835 | 836 | local field fields expr 837 | 838 | mapfile -t fields <<< "${1//$2/$'\n'}" 839 | 840 | for field in ${@:3}; do 841 | expr+=${fields[$((field > 0 ? field - 1 : field))]}' ' 842 | done 843 | 844 | echo "$expr" 845 | 846 | return $? 847 | } 848 | 849 | # .FUNCTION string.slice -> [str]|[bool] 850 | # 851 | # Retorna uma substring resultante do intervalo dentro de uma cadeia 852 | # de caracteres. O slice é a represetação do intervalo a ser capturado 853 | # e precisa respeitar o seguinte formato: 854 | # 855 | # [start:len]... 856 | # 857 | # start - Posição inicial dentro da cadeia. 858 | # len - Comprimento a ser capturado a partir de 'start'. 859 | # 860 | # > Não pode conter espaços entre slices. 861 | # > Utilize notação negativa para captura reversa. 862 | # 863 | # Pode ser especificado mais de um slice dentro da mesma expressão, 864 | # onde o slice subsequente trata a cadeia resultante do slice anterior 865 | # e assim respecitivamente. 866 | # 867 | # == EXEMPLO == 868 | # 869 | # source string.sh 870 | # 871 | # texto='Programação com shell script' 872 | # 873 | # string.slice "$texto" '[16:]' 874 | # string.slice "$texto" '[:11]' 875 | # string.slice "$texto" '[:-6]' 876 | # string.slice "$texto" '[-1]' 877 | # string.slice "$texto" '[4:10][2:9][:-2]' 878 | # 879 | # == SAÍDA == 880 | # 881 | # shell script 882 | # Programação 883 | # Programação com shell 884 | # t 885 | # mação 886 | # 887 | function string.slice() 888 | { 889 | getopt.parse 2 "expr:str:$1" "slice:str:$2" "${@:3}" 890 | 891 | [[ $2 =~ ${__BUILTIN__[slice]} ]] || error.fatal "'$2' erro de sintaxe na expressão slice" 892 | 893 | local str=$1 894 | local slice=$2 895 | local ini len 896 | 897 | while [[ $slice =~ \[([^]]+)\] ]]; do 898 | IFS=':' read ini len <<< "${BASH_REMATCH[1]}" 899 | [[ ${len#-} -gt ${#str} ]] && str='' && break 900 | [[ ${BASH_REMATCH[1]} != *@(:)* ]] && len=1 901 | ini=${ini:-0} 902 | len=${len:-$((${#str}-$ini))} 903 | str=${str:$ini:$len} 904 | slice=${slice/\[${BASH_REMATCH[1]}\]/} 905 | done 906 | 907 | echo "$str" 908 | 909 | return $? 910 | } 911 | 912 | # .TYPE string_t 913 | # 914 | # Implementa o objeto 'S' com os métodos: 915 | # 916 | # S.len 917 | # S.capitalize 918 | # S.center 919 | # S.count 920 | # S.endswith 921 | # S.startswith 922 | # S.expandspaces 923 | # S.find 924 | # S.rfind 925 | # S.isalnum 926 | # S.isalpha 927 | # S.isspace 928 | # S.isprint 929 | # S.islower 930 | # S.isupper 931 | # S.istitle 932 | # S.join 933 | # S.ljust 934 | # S.rjust 935 | # S.lower 936 | # S.upper 937 | # S.strip 938 | # S.lstrip 939 | # S.rstrip 940 | # S.replace 941 | # S.fnreplace 942 | # S.replacers 943 | # S.split 944 | # S.swapcase 945 | # S.title 946 | # S.reverse 947 | # S.repeat 948 | # S.zfill 949 | # S.compare 950 | # S.contains 951 | # S.fnsmap 952 | # S.fncmap 953 | # S.filter 954 | # S.field 955 | # S.slice 956 | # 957 | typedef string_t \ 958 | string.len \ 959 | string.capitalize \ 960 | string.center \ 961 | string.count \ 962 | string.endswith \ 963 | string.startswith \ 964 | string.expandspaces \ 965 | string.find \ 966 | string.rfind \ 967 | string.isalnum \ 968 | string.isalpha \ 969 | string.isspace \ 970 | string.isprint \ 971 | string.islower \ 972 | string.isupper \ 973 | string.istitle \ 974 | string.join \ 975 | string.ljust \ 976 | string.rjust \ 977 | string.lower \ 978 | string.upper \ 979 | string.strip \ 980 | string.lstrip \ 981 | string.rstrip \ 982 | string.replace \ 983 | string.fnreplace \ 984 | string.replacers \ 985 | string.split \ 986 | string.swapcase \ 987 | string.title \ 988 | string.reverse \ 989 | string.repeat \ 990 | string.zfill \ 991 | string.compare \ 992 | string.contains \ 993 | string.map \ 994 | string.fnsmap \ 995 | string.fncmap \ 996 | string.filter \ 997 | string.field \ 998 | string.slice 999 | 1000 | # Funções 1001 | readonly -f string.len \ 1002 | string.capitalize \ 1003 | string.center \ 1004 | string.count \ 1005 | string.endswith \ 1006 | string.startswith \ 1007 | string.expandspaces \ 1008 | string.find \ 1009 | string.rfind \ 1010 | string.isalnum \ 1011 | string.isalpha \ 1012 | string.isspace \ 1013 | string.isprint \ 1014 | string.islower \ 1015 | string.isupper \ 1016 | string.istitle \ 1017 | string.join \ 1018 | string.ljust \ 1019 | string.rjust \ 1020 | string.lower \ 1021 | string.upper \ 1022 | string.strip \ 1023 | string.lstrip \ 1024 | string.rstrip \ 1025 | string.replace \ 1026 | string.fnreplace \ 1027 | string.replacers \ 1028 | string.split \ 1029 | string.swapcase \ 1030 | string.title \ 1031 | string.reverse \ 1032 | string.repeat \ 1033 | string.zfill \ 1034 | string.compare \ 1035 | string.contains \ 1036 | string.map \ 1037 | string.fnsmap \ 1038 | string.fncmap \ 1039 | string.filter \ 1040 | string.field \ 1041 | string.slice 1042 | 1043 | # /* __STRING_SH__ */ 1044 | -------------------------------------------------------------------------------- /src/struct.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __STRUCT_SH__ ] && return 0 21 | 22 | readonly __STRUCT_SH__=1 23 | 24 | # Imports 25 | source builtin.sh 26 | 27 | # Protótipos 28 | readonly -A __STRUCT__=( 29 | [member]='^[a-zA-Z][a-zA-Z0-9_]*$' 30 | ) 31 | 32 | # .FUNCTION struct.__add__ ... [bool] 33 | # 34 | # Adiciona membro a estrutura com o tipo especificado. 35 | # > Pode ser especificado mais de um membro. 36 | # 37 | # == EXEMPLO == 38 | # 39 | # source struct.sh 40 | # 41 | # # Implementando uma estrutura. 42 | # # Utilize o sufixo '_st' para facilitar a identificação do tipo. (recomendado) 43 | # var cliente_st struct_t 44 | # 45 | # # Definindo membros e tipos. 46 | # cliente_st.__add__ nome str \ 47 | # sobrenome str \ 48 | # idade uint \ 49 | # telefone str 50 | # 51 | # 52 | # # Implementando a nova estrutura. 53 | # var cliente cliente_st 54 | # 55 | # # Atribuindo valores. 56 | # cliente.nome = 'Lucas' 57 | # cliente.sobrenome = 'Morais' 58 | # cliente.idade = 27 59 | # cliente.telefone = 98841-1232 60 | # 61 | # # Acessando valores 62 | # cliente.nome 63 | # cliente.sobrenome 64 | # cliente.idade 65 | # cliente.telefone 66 | # 67 | # == SAÍDA == 68 | # 69 | # Lucas 70 | # Morais 71 | # 27 72 | # 98841-1232 73 | # 74 | function struct.__add__() 75 | { 76 | getopt.parse -1 "obj:struct_t:$1" "member:str:$2" ... "${@:3}" 77 | 78 | local __obj__=$1 79 | local -n __ref__=$1 80 | local __mbrs__ 81 | 82 | # Define o objeto como map para armazenar os campos/valores da estrutura. 83 | declare -Ag $__obj__ || error.fatal "'$__obj__' não foi possível definir a estrutura do objeto" 84 | 85 | # Analisa argumentos posicionais subsequentes. 86 | set "${@:2}" 87 | 88 | while [[ $@ ]] 89 | do 90 | [[ $1 =~ ${__STRUCT__[member]} ]] || error.fatal "'$1' não é um identificador válido" 91 | [[ $2 =~ ${__BUILTIN__[vartype]} ]] || error.fatal "'$2' erro de sintaxe" 92 | [[ ${2%[*} == @(${__ALL_TYPE_NAMES__// /|}) ]] || error.fatal "'$2' tipo do objeto desconhecido" 93 | 94 | struct.__get__ $__obj__ $1 $2 # Lê estrutura 95 | shift 2 # desloca os argumentos tratados. 96 | done 97 | 98 | # Implementa os membros da estrutura. 99 | typedef $__obj__ ${__ref__[__MEMBERS__:$__obj__]} 100 | 101 | return $? 102 | } 103 | 104 | # .FUNCTION struct.__members__ -> [str]|[bool] 105 | function struct.__members__() 106 | { 107 | getopt.parse 1 "obj:struct_t:$1" "${@:2}" 108 | 109 | local -n __ref__=$1 110 | local __mbr__ 111 | 112 | for __mbr__ in ${__ref__[__MEMBERS__:$1]}; do 113 | echo "${__mbr__#*.}" 114 | done 115 | 116 | return $? 117 | } 118 | 119 | # .FUNCTION struct.__repr__ -> [str|str]|[bool] 120 | function struct.__repr__() 121 | { 122 | getopt.parse 1 "obj:struct_t:$1" "${@:2}" 123 | 124 | local -n __ref__=$1 125 | printf '%s\n' ${__ref__[__REPR__:$1]} 126 | 127 | return $? 128 | } 129 | 130 | # .FUNCTION struct.__kind__ -> [str]|[bool] 131 | function struct.__kind__() 132 | { 133 | getopt.parse 2 "obj:struct_t:$1" "member:str:$2" "${@:3}" 134 | 135 | local __obj__=$1[__KIND__:$2] 136 | 137 | [[ -v $__obj__ ]] || error.fatal "'$2' não é membro da estrutura '$1'" 138 | echo ${!__obj__} 139 | 140 | return $? 141 | } 142 | 143 | # struct.__get__ 144 | struct.__get__() 145 | { 146 | local __mem__ __kind__ 147 | 148 | # Se o membro for implementado por outra estrutura inicia uma leitura 149 | # recursiva até que todos elementos sejam definidos. 150 | if [[ $(typeof $3) == struct_t && $3 =~ ${__BUILTIN__[varname]} ]]; then 151 | for __mem__ in $($3.__members__); do 152 | # Obtem o tipo do objeto na sub-estrutura 153 | __kind__=$($3.__kind__ $__mem__) 154 | [[ $(typeof $__kind__) == struct_t ]] && 155 | struct.__get__ $1 $2.$__mem__ $__kind__ || # Lê a próxima estrutura. (recursivo) 156 | struct.__set__ $1 $2.$__mem__ $__kind__ # Define o membro. 157 | done 158 | else 159 | # (não estrutura) 160 | struct.__set__ $1 $2 $3 161 | fi 162 | 163 | return $? 164 | } 165 | 166 | # struct.__set__ 167 | struct.__set__() 168 | { 169 | local -n __ref__=$1 170 | 171 | # Verifica conflitos 172 | [[ $2 == @(${__mbrs__}) ]] && error.fatal "'$2' conflito de membros na estrutura" 173 | 174 | # Define o protótipo do membro na estutura determinando o nome e tipo suportado. 175 | # O tratamento dos argumentos do campo é condicional, ou seja, o comportamento 176 | # irá depender de como o membro é chamado. 177 | # 178 | # Exemplo: 179 | # 180 | # estrutura.membro -> Obtém valor 181 | # estrutura.membro = 'foo' -> Atribui valor 182 | # 183 | local __struct__='%s() 184 | { 185 | # Converte o objeto para o tipo "map" (global). 186 | declare -Ag ${1%%%%[*} || error.fatal "não foi possível inicializar a estrutura." 187 | 188 | local -n __ref__=${1%%%%[*} # referência 189 | local __vet__ 190 | 191 | # Captura o índice do objeto (se existir). 192 | [[ $1 =~ \[[0-9]+\] ]] 193 | __vet__=$BASH_REMATCH 194 | 195 | # Retorna o valor armazenado na chave se a implementação for chamada sem argumentos. 196 | [[ ${#@} -eq 1 ]] && echo "${__ref__[${__vet__}${FUNCNAME#*.}]}" && return $? 197 | 198 | # Trata os valores. 199 | getopt.parse 3 "__obj__:var:${1%%%%[*}" "operator:char:$2" "${FUNCNAME##*.}:%s%s:$3" "${@:4}" 200 | 201 | [[ $2 != = ]] && error.fatal "\\"$2\\" operador de atribuição inválido" 202 | 203 | # Salva o valor em sua respectiva chave. 204 | __ref__[${__vet__}${FUNCNAME#*.}]=$3 205 | 206 | return $? 207 | }' 208 | 209 | # Inicializa protótipo. 210 | printf -v __struct__ "$__struct__" "$1.$2" "$3" 211 | eval "$__struct__" || error.fatal "'$1.$2' não foi possível definir o membro da estrutura" 212 | 213 | # Anexa membro 214 | __mbrs__+=${__mbrs__:+|}${2} 215 | 216 | # Registra a estrutura 217 | __ref__[__KIND__:$2]=$3 # Tipo 218 | __ref__[__MEMBERS__:$1]+=$1.$2' ' # Membros (estrutura.membro) 219 | __ref__[__REPR__:$1]+=$1.$2'|'$3' ' # Representação (estrutura.membro|tipo) 220 | __ref__[__STRUCT__:$1]=true # Status 221 | 222 | return $? 223 | } 224 | 225 | # .TYPE struct_t 226 | # 227 | # O tipo 'struct_t' implementa métodos para construção de uma 228 | # estrutura genérica com definição de membros e tipos. 229 | # 230 | # Implementa o objeto 'S" com os métodos: 231 | # 232 | # S.__add__ 233 | # S.__members__ 234 | # S.__repr__ 235 | # S.__kind__ 236 | # 237 | typedef struct_t \ 238 | struct.__add__ \ 239 | struct.__members__ \ 240 | struct.__kind__ \ 241 | struct.__repr__ 242 | 243 | readonly -f struct.__add__ \ 244 | struct.__members__ \ 245 | struct.__kind__ \ 246 | struct.__repr__ \ 247 | struct.__set__ \ 248 | struct.__get__ 249 | 250 | # /* __STRUCT_SH__ */ 251 | -------------------------------------------------------------------------------- /src/textutil.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | # 20 | 21 | [ -v __TEXTUTIL_SH__ ] && return 0 22 | 23 | readonly __TEXTUTIL_SH__=1 24 | 25 | source builtin.sh 26 | source struct.sh 27 | source textutil.fonts 28 | 29 | # .FUNCTION textutil.fonts -> [str]|[bool] 30 | # 31 | # Retorna as fontes disponíveis. 32 | # 33 | function textutil.fonts() 34 | { 35 | getopt.parse 0 "$@" 36 | printf '%s\n' "${!__FONT_SIZE__[@]}" 37 | return $? 38 | } 39 | 40 | # .FUNCTION textutil.text => [str]|[bool] 41 | # 42 | # Imprime o texto com os atributos especificados. 43 | # 44 | function textutil.text() 45 | { 46 | getopt.parse 5 "text:str:$1" "align:uint:$2" "foreground:uint:$3" "background:uint:$4" "attr:uint:$5" "${@:6}" 47 | 48 | echo -en "${__C_ESC__}[${5};${4};${3}m" 49 | textutil.align "$1" "$2" 50 | echo -en "${__C_ESC__}[0;m" 51 | 52 | return $? 53 | } 54 | 55 | # .FUNCTION textutil.label = [str]|[bool] 56 | # 57 | # Imprime um label com os atributos especificados. 58 | # 59 | function textutil.label() 60 | { 61 | getopt.parse 6 "text:str:$1" "font:str:$2" "mode:uint:$3" "align:uint:$4" "foreground:uint:$5" "background:uint:$6" "${@:7}" 62 | 63 | local i ch asc line spc cols c fg 64 | 65 | [ ! -v __FONT_SIZE__[$2] ] && error.fatal "'$2' fonte não encontrada" 66 | 67 | IFS=' ' read _ cols < <(stty size) 68 | 69 | for ((c=30, i=0; i<${__FONT_SIZE__[$2]}; i++, c++)); do 70 | 71 | while read -n1 ch; do 72 | printf -v asc '%d' \'"${ch:- }" 73 | line+=${__FONT__[$2:$asc:$i]} 74 | done <<< "$1" 75 | 76 | case $3 in 77 | 1) ((c > 37)) && c=30; fg=$c;; 78 | 2) fg=$(((RANDOM%8)+30));; 79 | *) fg=$5;; 80 | esac 81 | 82 | case $4 in 83 | 1) spc=$(((cols/2)+(${#line}/2)+1));; 84 | 2) spc=$cols;; 85 | *) spc=0;; 86 | esac 87 | 88 | echo -en "${__C_ESC__}[${6};${fg}m" 89 | printf '%*s\n' $spc "$line" 90 | 91 | line='' 92 | done 93 | 94 | echo -en "${__C_ESC__}[0;m" 95 | 96 | return 0 97 | } 98 | 99 | # .FUNCTION textutil.align -> [str]|[bool] 100 | # 101 | # Imprime o texto aplicando o alinhamento especificado. 102 | # 103 | function textutil.align() 104 | { 105 | getopt.parse 2 "text:str:$1" "align:uint:$2" "${@:3}" 106 | 107 | local cols line spc 108 | 109 | read _ cols < <(stty size) 110 | 111 | while read line; do 112 | case $2 in 113 | 1) spc=$(((cols/2)+(${#line}/2)+1));; 114 | 2) spc=$cols;; 115 | *) spc=0;; 116 | esac 117 | printf '%*s\n' $spc "${line}" 118 | done <<< "$1" 119 | 120 | return 0 121 | } 122 | 123 | # .FUNCTION textutil.showlabel -> [str]|[bool] 124 | # 125 | # Imprime o label com os atributos da estrutura. 126 | # 127 | function textutil.showlabel() 128 | { 129 | getopt.parse 1 "label:label_st:$1" "${@:2}" 130 | 131 | local __text__=$($1.text) \ 132 | __font__=$($1.font) \ 133 | __mode__=$($1.mode) \ 134 | __align__=$($1.align) \ 135 | __cfg__=$($1.color.fg) \ 136 | __cbg__=$($1.color.bg) 137 | 138 | textutil.label "${__text__}" \ 139 | "${__font__}" \ 140 | "${__mode__:-0}" \ 141 | "${__align__:-0}" \ 142 | "${__cfg__:-0}" \ 143 | "${__cbg__:-0}" 144 | 145 | return $? 146 | } 147 | 148 | # .FUNCTION textutil.showtext -> [str]|[bool] 149 | # 150 | # Imprime o texto com os atributos da estrutura. 151 | # 152 | function textutil.showtext() 153 | { 154 | getopt.parse 1 "text:text_st:$1" "${@:2}" 155 | 156 | local __text__=$($1.text) \ 157 | __align__=$($1.align) \ 158 | __attr__=$($1.attr) \ 159 | __cfg__=$($1.color.fg) \ 160 | __cbg__=$($1.color.bg) \ 161 | __posx__=$($1.pos.x) \ 162 | __posy__=$($1.pos.y) 163 | 164 | echo -en "${__C_ESC__}[${__posy__:-0};${__posx__:-0}H${__C_ESC__}[${__attr__:-0};${__cbg__:-0};${__cfg__:-0}m" 165 | textutil.align "${__text__}" "${__align__:-0}" 166 | echo -en "${__C_ESC__}[0;m" 167 | 168 | return 0 169 | } 170 | 171 | # .FUNCTION textutil.index -> [str]|[bool] 172 | # 173 | # Retorna uma lista iterável dos elementos de 'list' no formato de índice, 174 | # iniciando a partir do índice 'start' com o comprimento do campo para 'len' dígitos. 175 | # 176 | function textutil.index() 177 | { 178 | getopt.parse 3 "iterable:array:$1" "len:uint:$2" "start:int:$3" "${@:4}" 179 | 180 | local -n __ref__=$1 181 | local __i__ __dot__ __item__ __cols__ 182 | 183 | __i__=$3 184 | 185 | IFS=' ' read _ __cols__ < <(stty size) 186 | 187 | for __item__ in "${__ref__[@]}"; do 188 | printf -v __dot__ '%*s' $(((__cols__-${#__item__})-($2+2))) 189 | printf "%s%$(($2+1))d\n" "${__item__} ${__dot__// /.}" "$__i__" 190 | ((__i__++)) 191 | done 192 | 193 | return $? 194 | } 195 | 196 | # .FUNCTION textutil.color -> [bool] 197 | # 198 | # Define a paleta de cores. (constantes 'FG_*' e 'BG_*') 199 | # 200 | function textutil.color() 201 | { 202 | getopt.parse 2 "foreground:uint:$1" "background:uint:$2" "${@:3}" 203 | 204 | echo -en "${__C_ESC__}[${2};${1}m" 205 | return $? 206 | } 207 | 208 | # .FUNCTION textutil.attr 209 | # 210 | # Define os atributos do texto. (constantes 'AT_*') 211 | # 212 | function textutil.attr() 213 | { 214 | getopt.parse 1 "attr:uint:$1" "${@:2}" 215 | 216 | echo -en "${__C_ESC__}[$1m" 217 | return $? 218 | } 219 | 220 | # .FUNCTION textutil.gotoxy -> [bool] 221 | # 222 | # Posiciona o cursor nas coordenadas 'x' e 'y'. 223 | # 224 | function textutil.gotoxy() 225 | { 226 | getopt.parse 2 "x:uint:$1" "y:uint:$2" "${@:3}" 227 | 228 | echo -en "${__C_ESC__}[${2};${1}H" 229 | return $? 230 | } 231 | 232 | # .FUNCTION textutil.hcpos -> [bool] 233 | # 234 | # Posiciona o cursor na coordenada inicial 'x=0,y=0'. 235 | # 236 | function textutil.hcpos() 237 | { 238 | getopt.parse 0 "$@" 239 | 240 | echo -en "${__C_ESC__}[H" 241 | return $? 242 | } 243 | 244 | # .FUNCTION textutil.scpos -> [bool] 245 | # 246 | # Salva a posição do cursor. 247 | # 248 | function textutil.scpos() 249 | { 250 | getopt.parse 0 "$@" 251 | 252 | echo -ne "${__C_ESC__}7" 253 | return $? 254 | } 255 | 256 | # .FUNCTION textutil.rcpos -> [bool] 257 | # 258 | # Restaura a posição do cursor. 259 | # 260 | function textutil.rcpos() 261 | { 262 | getopt.parse 0 "$@" 263 | 264 | echo -en "${__C_ESC__}8" 265 | return $? 266 | } 267 | 268 | # .FUNCTION textutil.cursor -> [bool] 269 | # 270 | # Habilita/desabilita a exibição do cursor. 271 | # 272 | function textutil.cursor() 273 | { 274 | getopt.parse 1 "status:bool:$1" "${@:2}" 275 | 276 | $1 && echo -en "${__C_ESC__}[?25h" || 277 | echo -en "${__C_ESC__}[?25l" 278 | 279 | return $? 280 | } 281 | 282 | # .FUNCTION textutil.clrctoe -> [bool] 283 | # 284 | # Limpa partir da posição atual do cursor até o final da linha. 285 | # 286 | function textutil.clrctoe() 287 | { 288 | getopt.parse 0 "$@" 289 | 290 | echo -en "${__C_ESC__}[K" 291 | return $? 292 | } 293 | 294 | # .FUNCTION textutil.clrbtoc -> [bool] 295 | # 296 | # Limpa do inicio da linha até a posição atual do cursor. 297 | # 298 | function textutil.clrbtoc() 299 | { 300 | getopt.parse 0 "$@" 301 | 302 | echo -en "${__C_ESC__}[1K" 303 | return $? 304 | } 305 | 306 | # .FUNCTION textutil.clrc -> [bool] 307 | # 308 | # Limpa a posição atual do cursor. 309 | # 310 | function textutil.clrc() 311 | { 312 | getopt.parse 0 "$@" 313 | 314 | echo -en "${__C_ESC__}[2K" 315 | return $? 316 | } 317 | 318 | # .FUNCTION textutil.curpos -> [bool] 319 | # 320 | # Obtém as coordenadas do cursor. 321 | # 322 | function textutil.curpos() 323 | { 324 | getopt.parse 1 "curpos:map:$1" "${@:2}" 325 | 326 | local -n __ref__=$1 327 | local __pos__ 328 | 329 | echo -en "${__C_ESC__}[6n" 330 | read -sd R __pos__ 331 | 332 | __pos__=${__pos__#*[} 333 | __ref__[x]=${__pos__#*;} 334 | __ref__[y]=${__pos__%;*} 335 | 336 | return $? 337 | } 338 | 339 | readonly __C_ESC__='\x1b' 340 | 341 | # .MAP curpos 342 | # 343 | # Chaves: 344 | # 345 | # x 346 | # y 347 | # 348 | 349 | # .CONST AT_ 350 | # 351 | # AT_RESET 352 | # AT_BOLD 353 | # AT_DIM 354 | # AT_SMSO 355 | # AT_UNDER 356 | # AT_BLINK 357 | # AT_REVERSE 358 | # AT_HIDDEN 359 | # 360 | readonly AT_RESET=0 361 | readonly AT_BOLD=1 362 | readonly AT_DIM=2 363 | readonly AT_SMSO=3 364 | readonly AT_UNDER=4 365 | readonly AT_BLINK=5 366 | readonly AT_REVERSE=7 367 | readonly AT_HIDDEN=8 368 | 369 | # .CONST FG_ 370 | # 371 | # FG_BLACK 372 | # FG_RED 373 | # FG_GREEN 374 | # FG_YELLOW 375 | # FG_BLUE 376 | # FG_MAGENTA 377 | # FG_CYAN 378 | # FG_WHITE 379 | # 380 | readonly FG_BLACK=30 381 | readonly FG_RED=31 382 | readonly FG_GREEN=32 383 | readonly FG_YELLOW=33 384 | readonly FG_BLUE=34 385 | readonly FG_MAGENTA=35 386 | readonly FG_CYAN=36 387 | readonly FG_WHITE=37 388 | 389 | # .CONST BG_ 390 | # 391 | # BG_BLACK 392 | # BG_RED 393 | # BG_GREEN 394 | # BG_YELLOW 395 | # BG_BLUE 396 | # BG_MAGENTA 397 | # BG_CYAN 398 | # BG_WHITE 399 | # 400 | readonly BG_BLACK=40 401 | readonly BG_RED=41 402 | readonly BG_GREEN=42 403 | readonly BG_YELLOW=43 404 | readonly BG_BLUE=44 405 | readonly BG_MAGENTA=45 406 | readonly BG_CYAN=46 407 | readonly BG_WHITE=47 408 | 409 | # .CONST TA_ 410 | # 411 | # TA_LEFT 412 | # TA_CENTER 413 | # TA_RIGHT 414 | # 415 | readonly TA_LEFT=0 416 | readonly TA_CENTER=1 417 | readonly TA_RIGHT=2 418 | 419 | # .CONST VM_ 420 | # 421 | # VM_NORMAL 422 | # VM_IRIS 423 | # VM_RANDOM 424 | # 425 | readonly VM_NORMAL=0 426 | readonly VM_IRIS=1 427 | readonly VM_RANDOM=2 428 | 429 | # Estruturas 430 | var color_st struct_t 431 | var text_st struct_t 432 | var pos_st struct_t 433 | var label_st struct_t 434 | 435 | # .STRUCT pos_st 436 | # 437 | # Implementa o objeto 'S' com os membros: 438 | # 439 | # S.x [uint] 440 | # S.y [uint] 441 | # 442 | pos_st.__add__ x uint \ 443 | y uint 444 | 445 | # .STRUCT color_st 446 | # 447 | # Implementa o objeto 'S' com os membros: 448 | # 449 | # S.fg [uint] 450 | # S.bg [uint] 451 | # 452 | color_st.__add__ fg uint \ 453 | bg uint 454 | 455 | # .STRUCT text_st 456 | # 457 | # Implementa o objeto 'S' com os membros: 458 | # 459 | # S.text [str] 460 | # S.align [uint] 461 | # S.attr [uint] 462 | # S.color [color_st] 463 | # S.pos [pos_st] 464 | # 465 | text_st.__add__ text str \ 466 | align uint \ 467 | attr uint \ 468 | color color_st \ 469 | pos pos_st 470 | 471 | # .STRUCT label_st 472 | # 473 | # Implementa o objeto 'S' com os membros: 474 | # 475 | # S.text [str] 476 | # S.font [str] 477 | # S.mode [uint] 478 | # S.align [uint] 479 | # S.color [color_st] 480 | # 481 | label_st.__add__ text str \ 482 | font str \ 483 | mode uint \ 484 | align uint \ 485 | color color_st 486 | 487 | # .TYPE textutil_t 488 | # 489 | # Implementa o objeto 'S' com os métodos: 490 | # 491 | # S.text 492 | # S.align 493 | # S.index 494 | # S.label 495 | # 496 | typedef textutil_t textutil.text \ 497 | textutil.align \ 498 | textutil.index \ 499 | textutil.label 500 | 501 | # Funções (somente-leitura) 502 | readonly -f textutil.fonts \ 503 | textutil.text \ 504 | textutil.label \ 505 | textutil.align \ 506 | textutil.showlabel \ 507 | textutil.showtext \ 508 | textutil.index \ 509 | textutil.color \ 510 | textutil.attr \ 511 | textutil.gotoxy \ 512 | textutil.hcpos \ 513 | textutil.scpos \ 514 | textutil.rcpos \ 515 | textutil.cursor \ 516 | textutil.clrctoe \ 517 | textutil.clrbtoc \ 518 | textutil.clrc \ 519 | textutil.curpos 520 | 521 | # /* __TEXTUTIL_SH__ */ 522 | -------------------------------------------------------------------------------- /src/time.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __TIME_SH__ ] && return 0 21 | 22 | source builtin.sh 23 | 24 | # .FUNCTION time.time -> [uint] 25 | # 26 | # Retorna o tempo em segundos decorridos desde 1970-01-01 00:00:00 UTC 27 | # 28 | function time.time() 29 | { 30 | getopt.parse 0 "$@" 31 | printf '%(%s)T\n' 32 | return $? 33 | } 34 | 35 | # .FUNCTION time.now -> [str] 36 | # 37 | # Retorna uma representação da data e hora atual. 38 | # 39 | function time.now() 40 | { 41 | getopt.parse 0 "$@" 42 | printf "%(%a %b %d %H:%M:%S %Y %z)T\n" 43 | } 44 | 45 | # .FUNCTION time.localtime -> [bool] 46 | # 47 | # Converte a hora atual em um mapa time. 48 | # 49 | function time.localtime() 50 | { 51 | getopt.parse 1 "time:map:$1" "${@:2}" 52 | 53 | local -n __ref__=$1 54 | local __tm__ 55 | 56 | IFS=' ' read -a __tm__ <<< $(printf "%(%_m %_d %_H %_M %_S %_Y %_j %_w %z)T") 57 | 58 | __ref__[tm_mon]=${__tm__[0]} 59 | __ref__[tm_mday]=${__tm__[1]} 60 | __ref__[tm_hour]=${__tm__[2]} 61 | __ref__[tm_min]=${__tm__[3]} 62 | __ref__[tm_sec]=${__tm__[4]} 63 | __ref__[tm_year]=${__tm__[5]} 64 | __ref__[tm_yday]=${__tm__[6]} 65 | __ref__[tm_wday]=${__tm__[7]} 66 | __ref__[tm_zone]=${__tm__[8]} 67 | __ref__[tm_isdst]=$(time.__getdst__) 68 | 69 | return $? 70 | } 71 | 72 | # .FUNCTION time.gmtime -> [bool] 73 | # 74 | # Converte o tempo em segundos em um mapa de tempo. 75 | # 76 | function time.gmtime() 77 | { 78 | getopt.parse 2 "seconds:uint:$1" "time:map:$2" "${@:3}" 79 | 80 | local -n __ref__=$2 81 | local __tm__ 82 | 83 | IFS=' ' read -a __tm__ <<< $(printf "%(%_m %_d %_H %_M %_S %_Y %_j %_w %z)T" $1) 84 | 85 | __ref__[tm_mon]=${__tm__[0]} 86 | __ref__[tm_mday]=${__tm__[1]} 87 | __ref__[tm_hour]=${__tm__[2]} 88 | __ref__[tm_min]=${__tm__[3]} 89 | __ref__[tm_sec]=${__tm__[4]} 90 | __ref__[tm_year]=${__tm__[5]} 91 | __ref__[tm_yday]=${__tm__[6]} 92 | __ref__[tm_wday]=${__tm__[7]} 93 | __ref__[tm_zone]=${__tm__[8]} 94 | __ref__[tm_isdst]=$(time.__getdst__) 95 | 96 | return $? 97 | } 98 | 99 | # .FUNCTION time.ctime -> [str]|[bool] 100 | # 101 | # Converte o tempo em segundos para uma string de hora local. 102 | # 103 | function time.ctime() 104 | { 105 | getopt.parse 1 "seconds:uint:$1" "${@:2}" 106 | 107 | printf "%(%a %b %d %H:%M:%S %Y %z)T\n" $1 108 | return $? 109 | } 110 | 111 | # .FUNCTION time.timezone -> [str]|[bool] 112 | # 113 | # Retorna o fuso horário do sistema. 114 | # 115 | function time.timezone() 116 | { 117 | getopt.parse 0 "$@" 118 | echo "$(< /etc/timezone)" 119 | return $? 120 | } 121 | 122 | # .FUNCTION time.asctime -> [str]|[bool] 123 | # 124 | # Converte o mapa 'time' em uma string de representação de data e hora. 125 | # 126 | function time.asctime() 127 | { 128 | getopt.parse 1 "time:map:$1" "${@:2}" 129 | 130 | local -n __ref__=$1 131 | local __strm__=([1]=Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec) 132 | local __strw__=(Sun Mon Tue Wed Thu Fri Sat) 133 | 134 | if ! (time.__chktime__ ${__ref__[tm_hour]} \ 135 | ${__ref__[tm_min]} \ 136 | ${__ref__[tm_sec]} && 137 | time.__chkdate__ ${__ref__[tm_wday]} \ 138 | ${__ref__[tm_mday]} \ 139 | ${__ref__[tm_mon]} \ 140 | ${__ref__[tm_year]} \ 141 | ${__ref__[tm_yday]}); then 142 | error.error "'$1' mapa time inválido" 143 | return 1 144 | fi 145 | 146 | printf "%s %s %d %02d:%02d:%02d %d %(%z)T\n" \ 147 | ${__strw__[${__ref__[tm_wday]}]} \ 148 | ${__strm__[${__ref__[tm_mon]}]} \ 149 | ${__ref__[tm_mday]} \ 150 | ${__ref__[tm_hour]} \ 151 | ${__ref__[tm_min]} \ 152 | ${__ref__[tm_sec]} \ 153 | ${__ref__[tm_year]} 154 | 155 | return $? 156 | } 157 | 158 | function time.__getdst__() 159 | { 160 | local tzinfo 161 | 162 | while read tzinfo; do 163 | [[ $tzinfo =~ isdst=(0|1) ]] && break 164 | done < <(zdump -v $(< /etc/timezone)) 165 | 166 | echo "${BASH_REMATCH[1]}" 167 | 168 | return $? 169 | } 170 | 171 | function time.__chktime__() 172 | { 173 | # hour=$1, min=$2, sec=$3 174 | [[ $1 -ge 0 && $1 -le 23 ]] && 175 | [[ $2 -ge 0 && $2 -le 59 ]] && 176 | [[ $3 -ge 0 && $3 -le 59 ]] 177 | 178 | return $? 179 | } 180 | 181 | function time.__chkdate__() 182 | { 183 | local week=$1 day=$2 month=$3 year=$4 yearday=$5 d=$2 m=$3 y=$4 w tyd 184 | local days=('0 31 28 31 30 31 30 31 31 30 31 30 31' 185 | '0 31 29 31 30 31 30 31 31 30 31 30 31') 186 | 187 | leap_year=$(((year % 4 == 0 && year % 100 != 0) || year % 400 == 0 ? 1 : 0)) 188 | tyd=$((leap_year == 0 ? 365 : 366)) 189 | w=$(($((d+=m<3 ? y--: y-2,23*m/9+d+4+y/4-y/100+y/400))%7)) 190 | 191 | days=(${days[$leap_year]}) 192 | 193 | [[ $month -ge 1 && $month -le 12 ]] && 194 | [[ $day -ge 1 && $day -le ${days[$month]} ]] && 195 | [[ $year -ge 1900 ]] && 196 | [[ $w == $week ]] && 197 | [[ $yearday -ge 1 && $yearday -le $tyd ]] 198 | 199 | return $? 200 | } 201 | 202 | # .MAP time 203 | # 204 | # Chaves: 205 | # 206 | # tm_mon /* mês (1..12) */ 207 | # tm_mday /* dia do mês (1..31) */ 208 | # tm_hour /* hora (0..23) */ 209 | # tm_min /* minuto (0..59) */ 210 | # tm_sec /* segundo (0..60) */ 211 | # tm_year /* ano */ 212 | # tm_yday /* dia do ano (1..366) */ 213 | # tm_wday /* dia da semana (0..6); 0 é domingo */ 214 | # tm_zone /* Fuso horário */ 215 | # tm_isdst /* Horário de verão */ 216 | # 217 | 218 | # Funções (somente-leitura) 219 | readonly -f time.time \ 220 | time.now \ 221 | time.localtime \ 222 | time.gmtime \ 223 | time.ctime \ 224 | time.timezone \ 225 | time.asctime \ 226 | time.__getdst__ \ 227 | time.__chktime__ \ 228 | time.__chkdate__ 229 | 230 | # /* __TIME_SH__ */ 231 | -------------------------------------------------------------------------------- /src/user.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Copyright 2018 Juliano Santos [SHAMAN] 4 | # 5 | # This file is part of bashsrc. 6 | # 7 | # bashsrc is free software: you can redistribute it and/or modify 8 | # it under the terms of the GNU General Public License as published by 9 | # the Free Software Foundation, either version 3 of the License, or 10 | # (at your option) any later version. 11 | # 12 | # bashsrc is distributed in the hope that it will be useful, 13 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | # GNU General Public License for more details. 16 | # 17 | # You should have received a copy of the GNU General Public License 18 | # along with bashsrc. If not, see . 19 | 20 | [ -v __USER_SH__ ] && return 0 21 | 22 | readonly __USER_SH__=1 23 | 24 | source builtin.sh 25 | 26 | # .FUNCTION user.getuid -> [uint]|[bool] 27 | # 28 | # Retorna o id do usuário atual. 29 | # 30 | function user.getuid() 31 | { 32 | getopt.parse 0 "$@" 33 | 34 | echo $UID 35 | return $? 36 | } 37 | 38 | # .FUNCTION user.geteuid -> [uint]|[bool] 39 | # 40 | # Retorna o id efetivo do usuário atual. 41 | # 42 | function user.geteuid() 43 | { 44 | getopt.parse 0 "$@" 45 | 46 | echo $EUID 47 | return $? 48 | } 49 | 50 | # .FUNCTION user.getpwall -> [uint]|[bool] 51 | # 52 | # Retorna todos os usuários do sistema. 53 | # 54 | function user.getpwall 55 | { 56 | getopt.parse 0 "$@" 57 | 58 | local user 59 | 60 | while IFS=':' read user _; do 61 | echo $user 62 | done < /etc/passwd 63 | 64 | return $? 65 | } 66 | 67 | # .FUNCTION user.getpwnam -> [bool] 68 | # 69 | # Obtem informações do usuário. 70 | # 71 | # == EXEMPLO == 72 | # 73 | # source user.sh 74 | # 75 | # # Mapa 76 | # declare -A info=() 77 | # 78 | # user.getpwname 'colord' info 79 | # 80 | # echo ${info[pw_uid]} 81 | # echo ${info[pw_name]} 82 | # echo ${info[pw_dir]} 83 | # echo ${info[pw_gecos]} 84 | # 85 | # == SAÍDA == 86 | # 87 | # 113 88 | # colord 89 | # /var/lib/colord 90 | # colord colour management daemon,,, 91 | # 92 | function user.getpwnam() 93 | { 94 | getopt.parse 2 "name:str:$1" "pwd:map:$2" "${@:3}" 95 | 96 | local __name__ __passwd__ __uid__ __gid__ 97 | local __gecos__ __dir__ __shell__ 98 | local -n __ref__=$2 99 | 100 | __ref__=() || return 1 101 | 102 | while IFS=':' read __name__ \ 103 | __passwd__ \ 104 | __uid__ \ 105 | __gid__ \ 106 | __gecos__ \ 107 | __dir__ \ 108 | __shell__; do 109 | [[ $1 == $__name__ ]] && break 110 | done < /etc/passwd 111 | 112 | (($?)) && { error.error "'$1' usuário não encontrado"; return $?; } 113 | 114 | __ref__[pw_name]=$__name__ 115 | __ref__[pw_passwd]=$__passwd__ 116 | __ref__[pw_uid]=$__uid__ 117 | __ref__[pw_gid]=$__gid__ 118 | __ref__[pw_gecos]=$__gecos__ 119 | __ref__[pw_dir]=$__dir__ 120 | __ref__[pw_shell]=$__shell__ 121 | 122 | return $? 123 | } 124 | 125 | # .FUNCTION user.getpwuid -> [bool] 126 | # 127 | # Obtém informações do uid especificado. 128 | # 129 | # Inicializa o mapa 'S' com as chaves: 130 | # 131 | # S[pw_name] 132 | # S[pw_passwd] 133 | # S[pw_uid] 134 | # S[pw_gid] 135 | # S[pw_gecos] 136 | # S[pw_dir] 137 | # S[pw_shell] 138 | # 139 | function user.getpwuid() 140 | { 141 | getopt.parse 2 "uid:uint:$1" "pwd:map:$2" "${@:3}" 142 | 143 | local __name__ __passwd__ __uid__ __gid__ 144 | local __gecos__ __dir__ __shell__ 145 | local -n __ref__=$2 146 | 147 | __ref__=() || return 1 148 | 149 | while IFS=':' read __name__ \ 150 | __passwd__ \ 151 | __uid__ \ 152 | __gid__ \ 153 | __gecos__ \ 154 | __dir__ \ 155 | __shell__; do 156 | [[ $1 == $__uid__ ]] && break 157 | done < /etc/passwd 158 | 159 | (($?)) && { error.error "'$1' id não encontrado"; return $?; } 160 | 161 | __ref__[pw_name]=$__name__ 162 | __ref__[pw_passwd]=$__passwd__ 163 | __ref__[pw_uid]=$__uid__ 164 | __ref__[pw_gid]=$__gid__ 165 | __ref__[pw_gecos]=$__gecos__ 166 | __ref__[pw_dir]=$__dir__ 167 | __ref__[pw_shell]=$__shell__ 168 | 169 | return $? 170 | } 171 | 172 | # .MAP pwd 173 | # 174 | # Chaves: 175 | # 176 | # pw_name /* Nome do usuário do sistema */ 177 | # pw_passwd /* Senha criptografada ou asteriscos. */ 178 | # pw_uid /* Identificação númerica do usuário */ 179 | # pw_gid /* Identificação númerica do grupo primário */ 180 | # pw_gecos /* Informações do usuário (opcional) */ 181 | # pw_dir /* Diretório do usuário ($HOME). */ 182 | # pw_shell /* Interpretador de comandos */ 183 | # 184 | 185 | # Funções (somente leitura) 186 | readonly -f user.getuid \ 187 | user.geteuid \ 188 | user.getpwall \ 189 | user.getpwnam \ 190 | user.getpwuid 191 | 192 | # /* __USER_SH__ */ 193 | --------------------------------------------------------------------------------