Las salidas aritméticas de bash dan como resultado decimal

Oct 25 2020

Cuando hace cálculos con Bash y le da una entrada hexadecimal, hace el cálculo correctamente, pero genera los resultados en forma decimal. ¿Esto es normal? Obviamente quiero los resultados en hexadecimal ya que los estoy dando en hexadecimal. ¿Hay alguna forma de cambiar esto?

Lo sé printf, pero llamar printfcada vez que hago una actualización menor a una variable con un incremento o algo así, parece extraño.

Por ejemplo al hacer

echo $((0xa+1))

El resultado es 11, pero espero b.

Respuestas

8 StéphaneChazelas Oct 25 2020 at 02:41

$((0xa+1))es una expansión aritmética que se expande a la representación decimal de la evaluación de la 0xa+1expresión aritmética.

Esa expresión no es 0xseguida por a+1, eso es 0xa(constante entera hexadecimal), el +operador de suma binaria y 1(constante entera decimal).

Hasta donde yo sé, el único shell similar a Bourne que puede tener números de rendimiento de expansión aritmética enteros en cualquier base que no sea 10 es zshy debe especificar explícitamente en qué base desea la expansión con esta sintaxis:

$ echo $(( [#16] 0xa + 1 )) 16#B $ echo $(( [##16] 0xa + 1 )) B $ set -o c_bases
$ echo $(( [#16] 0xa + 1 ))
0xB

Y como en ksh, puede asignar una base a una variable entera con:

$ typeset -i16 var $ (( var = 0xa + 1 ))
$ echo "$var"
16#B
$ set -o c_bases $ echo "$var"
0xB

(ksh da 16#ballí, la c_basesopción (dar en 0xBADlugar de 16#BADpara hexadecimal y en 077lugar de 8#77cuando la octal_zeroesopción también está configurada) es zsh-específica).

Además, en zsh, si se asigna una variable dentro de una expresión aritmética de enteros (utilizando el =, --, ++, *=, -=, +=, etc. operadores aritméticos), entonces se da el tipo de número entero y (a menos que ya tenía una base asignada a ella) hereda la la base de la constante de más a la derecha entero que tiene una base explícita (como 10#12, 0x12, 0b11).

Por ejemplo, (( a = 0x10 + 0b1000 ))es como typeset -gi2 a=24. Se define acomo una variable entera con valor 24, con $aexpansión en binario ( 2#11000).

Consulte info zsh 'Arithmetic Evaluation'para obtener más detalles.

bashcopió typeset -ide ksh, pero no typeset -i<base>.

Para bashconvertir un número a una base distinta de 10, puede usar su printfincorporado para las bases 8 y 16 como han mostrado otros, o recurrir a dc/ bc/ ksh/ zshpara otras bases (el rango de bases admitidas y cómo se expresan varía entre esas ).

Por ejemplo, para convertir a base 30:

base30_dc() { echo "30o $1 p" | dc; } base30_bc() { echo "obase=30; $1" | bc; }
base30_ksh93() { ksh93 -c 'printf "%..30d\n" "$@"' ksh "$@"; }
base30_zsh() { zsh -c 'echo $(([##30] $1))' zsh "$1"; }

Que dan:

$ base30_dc 1234
 01 11 04
$ base30_bc 1234 01 11 04 $ base30_ksh93 1234
1b4
$ base30_zsh 1234
1B4

Sin embargo, tenga en cuenta que en dcnúmeros negativos las constantes se expresan con _el signo. Mientras -es el operador de resta binaria ( dcusa notación polaca inversa ).

$ base30_dc _1234
- 01 11 04
$ base30_dc '0 1234 -'
- 01 11 04

En cuanto al tipo de números que se reconocen dentro de las expresiones aritméticas, eso también varía entre capas.

POSIX requiere que se reconozcan al menos constantes decimales, octales y hexadecimales utilizando la sintaxis 123, 0123y 0x123respectivamente. A algunos shells les gusta mksho zshno reconocen 0123como octal de forma predeterminada, ya que hacerlo se interpone con más frecuencia de lo que es útil (como cuando se trata de números rellenados con 0), solo cuando se habilita algún modo de compatibilidad POSIX ( posixopción en mksh, octalzeroesopción o shemulación en zsh).

ksh, bash y zsh admiten la 12#123notación para ingresar números en bases arbitrarias (nuevamente, con el rango que varía entre ellos).

zsh admite 0b111 como alternativa a 2#111los números binarios. E incrustar _números internos para ayudar a la legibilidad (como 1_000_000o 0xdead_beef)

ksh93, zsh y números de punto flotante de apoyo Yash ( 0.123(o 0,123en ksh93 dependiendo de la configuración regional), 1e20, inf, nan...). ksh93 también admite la notación de punto flotante hexadecimal como 0xA.Bp-3 (o 0xA, Bp-3 según la configuración regional).

6 terdon Oct 25 2020 at 02:19

Bash (o cualquier otro shell, en realidad) no es una buena herramienta para la programación general. Bash ni siquiera maneja la aritmética de punto flotante, y mucho menos nada más complejo. El shell es un shell , puedes escribir pequeños scripts simples en uno, pero no debes pensar en él como un lenguaje de programación de propósito general: no lo es.

Me temo que si insiste en usar bash para algo como esto, se verá obligado a usar soluciones alternativas cada vez más complejas, comenzando con printf:

$ printf '%x\n' "$((0xa+1))"
b

La buena noticia es que al menos también puede hacer esto:

$ printf '%x\n' 11
b

Por lo tanto, puede convertir fácilmente de uno a otro, lo que significa que no necesita llamar printfcada vez:

var=0xa

((var++))
echo "Var (decimal): $var"
printf 'Var (hex): %x\n' "$var"

Ejecutar lo anterior imprimiría:

$ foo.sh
Var (decimal): 11
Var (hex): b

En otras palabras, puede hacer lo suyo en hexadecimal y no preocuparse por cómo se muestra hasta que necesite imprimir algo.

Pero no, no encontrará una forma nativa de hacer este tipo de cosas en el shell porque está intentando usar el shell para un propósito para el que no fue diseñado.