Salve, Meshtre Júlio! :)
Achei melhor responder à sua saudação apenas se me ocorresse algo para acrescentar à thread (escopo!)
Como o problema do Alfredo está resolvido, tomei a liberdade de divagar sobre o assunto. A análise abaixo pode parecer desnecessária para os mais pragmáticos. Peço que sobrelevem, se for o caso.
Devido à minha formação matemática, aprecio o fato do bash trabalhar nativamente com bases numéricas arbitrárias. Pena que (por enquanto?) ele faz nativamente isso apenas num sentido, o que nos obriga a recorrer ao bc, por ex, para converter de decimal para outras bases.
Um detalhe a ser mencionado é o intervalo de bases com as quais o bash trabalha nativamente:
echo $(( 1#1 )) # -bash: 1#1: base aritmética inválida (token de erro é "1#1")
echo $(( 2#1 )) # 1
echo $(( 3#1 )) # 1
echo $(( 16#1 )) # 1
echo $(( 60#1 )) # 1
echo $(( 70#1 )) # -bash: 70#1: base aritmética inválida (token de erro é "70#1")
echo $(( 65#1 )) # -bash: 65#1: base aritmética inválida (token de erro é "65#1")
echo $(( 64#1 )) # 1
Até a base 64, portanto.
O bc também tem sua limitação:
bc <<< "obase=10 ; ibase=36; 1" # 1
bc <<< "obase=10 ; ibase=37; 1" # Runtime warning (func=(main), adr=13): ibase too large, set to 36 1
bc <<< "obase=100 ; ibase=36; 1" # 01
bc <<< "obase=1000; ibase=36; 1" # 001
Até a base 36 (de entrada), mas aparentemente "nenhuma" limitação na base de saída. Uma consequência disso é que, ao converter para bases "grandes", coisas estranhas acontecem:
bc <<< "obase=16 ; ibase=10; 16771328" # FFE900
bc <<< "obase=17 ; ibase=10; 16771328" # 11 13 13 11 04 12
bc <<< "obase=64 ; ibase=10; 16771328" # 63 62 36 00
bc <<< "obase=100 ; ibase=10; 16771328" # 16 77 13 28
bc <<< "obase=1000; ibase=10; 16771328" # 016 771 328 (cada "dígito" é representado com seu valor decimal formatado com a quantidade de dígitos decimais do maior possível na base, 999 nesse caso)
Claramente o bc não assume caracteres para dígitos em bases de saída maiores que 16, apesar de fazê-lo para as 36 que reconhece de entrada:
bc <<< "obase=10; ibase=36; F" # 15
bc <<< "obase=10; ibase=36; G" # 16
bc <<< "obase=10; ibase=36; Z" # 35
bc <<< "obase=10; ibase=36; a" # 0
bc <<< "obase=10; ibase=36; z" # 0
E aceita apenas A..Z como dígitos das bases 11..36, não a..z.
No bash é um pouco diferente:
echo $((16#f)) # 15
echo $((16#F)) # 15
echo $((16#g)) # -bash: 16#g: valor muito grande para esta base de numeração (token de erro é "16#g")
echo $((16#G)) # -bash: 16#G: valor muito grande para esta base de numeração (token de erro é "16#G")
# (evidente: um dígito não pode ter valor decimal maior ou igual à base)
echo $((17#g)) # 16
echo $((17#G)) # 16
echo $((36#g)) # 16
echo $((36#G)) # 16
echo $((36#z)) # 35
echo $((36#Z)) # 35
Até a base 36 tanto faz usar letras maiúsculas ou minúsculas como dígitos.
A partir da base 37 até a última reconhecida (64), a coisa muda de figura:
echo $((37#z)) # 35
echo $((37#Z)) # -bash: 37#Z: valor muito grande para esta base de numeração (token de erro é "37#Z")
echo $((64#z)) # 35
echo $((64#Z)) # 61
Ou seja, em bases maiores que 37, o bash assume os dígitos na sequência 0..9, a..z e A..Z.
Mas Z é apenas o dígito 61. E os dígitos 62 e 63 (na base 64)?
Poderíamos recorrer à documentação (depois do oráculo) mas, né, por favor... :-p
candidatos="@ $ % - _ = + , . : / ? [ ] { }"
Já vamos de cara deixar fora os caracteres '"!#&*;()<> . Sendo especiais para o bash, dificilmente seriam adotados como dígito.
for c in $candidatos; { echo -n "# "; ( echo $c: $((64#$c)) ); }
# @: 62
# -bash: 64#$: erro de sintaxe: operador aritmético inválido (token de erro é "$")
# -bash: 64#%: erro de sintaxe: esperava operando (token de erro é "%")
# -bash: 64#-: erro de sintaxe: esperava operando (token de erro é "-")
# _: 63
# -bash: 64#=: tentativa de atribuição para algo que não é uma variável (token de erro é "=")
# -bash: 64#+: erro de sintaxe: esperava operando (token de erro é "+")
# -bash: 64#,: erro de sintaxe: esperava operando (token de erro é ",")
# -bash: 64#.: erro de sintaxe: operador aritmético inválido (token de erro é ".")
# -bash: 64#:: erro de sintaxe na expressão (token de erro é ":")
# -bash: 64#/: erro de sintaxe: esperava operando (token de erro é "/")
# -bash: 64#?: esperava uma expressão (token de erro é "?")
# -bash: 64#[: erro de sintaxe: operador aritmético inválido (token de erro é "[")
# -bash: 64#]: erro de sintaxe: operador aritmético inválido (token de erro é "]")
# -bash: 64#{: erro de sintaxe: operador aritmético inválido (token de erro é "{")
# -bash: 64#}: erro de sintaxe: operador aritmético inválido (token de erro é "}")
E descobrimos os dígitos 62 e 63 :)
Então:
echo $((64#a)) # 10
echo $((64#z)) # 35
echo $((64#A)) # 36
echo $((64#Z)) # 61
echo $((64#@)) # 62
echo $((64#_)) # 63
Resumindo:
- o bash converte nativamente até da base 64 para decimal, mas não faz o contrário (nativamente).
- o bc converte para "qualquer" base, mas apenas até a 36 de entrada, e mesmo assim, sem adotar dígitos para as bases de saída superiores a 17. E, como se não bastasse (indignado :-p), adota dígitos diferentes do bash até a base 36: letras maiúsculas, enquanto o bash adota minúsculas.
Conclusão: não dá pra escapar da implementação do algoritmo de conversão de decimal para uma base qualquer. Ainda que seja só por diversão :D
Dá até pra pensar em mais um motivozinho: o bc é lindo e maravilhoso, mas vamos supor que queiramos contar apenas com o bash, sem depender do bc instalado (por algum motivo questionável qualquer).
Então, agora que apresentei minha desculpa esfarrapada para implementar a conversão de decimal para outra base, seguem minhas sugestões:
d=( {0..9} {a..z} {A..Z} @ _ ) # array com a sequência de dígitos conforme adotados pelo bash
# conversão de um valor [D] decimal para um valor [B] na base [b] (2 por default, <=64, limite da notação nativa do bash)
# opcional: quantidade [n] de dígitos de D na base b (completados com 0's à esquerda).
# algoritmo repetitivo
d2bl () { local D=$1 b=${2:-2} n=${3:-0} B; while ((D>0||n>0)); do B=${d[$((D%b))]}$B; D=$((D/b)); ((n--)); done; echo $B; }
# algoritmo recursivo
d2br1() { local D=$1 b=${2:-2} n=${3:-0} B; ((D>0||n>0)) && echo $($FUNCNAME $((D/b)) $b $((n-1)))${d[$((D%b))]} || echo; }
# algoritmo recursivo "enxugado". Necessário indicar os 3 parâmetros (D b n)
d2br2() { (($1>0||$3>0)) && echo $( $FUNCNAME $(($1/$2)) $2 $(($3-1)) )${d[$(($1%$2))]} || echo; }
{
echo $((2#111101)) # 61
d2bl 61 # 111101
d2bl 61 2 # 111101
d2bl 61 2 8 # 00111101
d2br1 61 # 111101
d2br1 61 2 # 111101
d2br1 61 2 8 # 00111101
echo $((16#3d)) # 61
d2bl 61 16 # 3d
d2bl 61 16 4 # 003d
d2br1 61 16 # 3d
d2br1 61 16 4 # 003d
echo $((64#_@A0)) # 16771328
d2bl 16771328 64 # _@A0
d2br1 16771328 64 # _@A0
d2br2 16771328 64 0 # _@A0
d2br2 16771328 64 6 # 00_@A0
}
Att,