Я попытался написать преобразователь произвольной точности из шестнадцатеричного в десятичное в чистом sh (на самом деле ash, поскольку busybox sh
запускает встроенный ash).Это требует гораздо больше усилий, чем bash, из-за ограниченного набора функций (без массивов) и «странных» ошибок без четкой документации (например, пробелы в выражениях не допускаются)
#!/bin/ash
obase=1000000000 # 1e9, the largest power of 10 that fits in int32_t
ibase=$((1 << 7*4)) # only 7 hex digits, because 0xFFFFFFFF > 1e9
inp="000000${1#0x}" # input value in $1 with optional 0x
inp=${inp:$((${#inp}%7)):${#inp}} # pad the string length to a multiple of 7
carry=0
# workaround, since sh and ash don't support arrays
result0=0 # output digits will be stored in resultX variables in little endian
MSDindex=0 # index of the most significant digit in the result
print_result()
{
eval echo -n \$result$MSDindex # print MSD
if [ $MSDindex -gt 0 ]; then # print remaining digits
for i in $(seq $((MSDindex-1)) -1 0); do eval printf "%09d" \$result$i; done
fi
echo
}
# Multiply a digit with the result
# $1 contains the value to multiply with the result array
mul()
{
carry=0
for i in $(seq 0 $MSDindex); do
eval let res="$1\\*result$i+carry"
eval let result$i=res%obase
let carry=res/obase
done
while [ $carry -ne 0 ]; do
let MSDindex=MSDindex+1
eval let result$MSDindex=carry%obase
let carry=carry/obase
done
}
# Add a digit with the result
# $1 contains the digit to add with the array
add()
{
eval let res=$1+result0
eval let result0=res%obase
let carry=res/obase
i=1
while [ $carry -ne 0 ]
do
eval let res=carry+result$i
eval let result$i=res%obase
let carry=res/obase
if [ $i -gt $MSDindex ]; then MSDindex=$i; fi
let i=i+1
done
}
# main conversion loop
while [ -n "$inp" ] # iterate through the hex digits, 7 at a time
do
hexdigit=${inp:0:7}
mul $ibase # result = result*input_base+hexdigit
add 0x$hexdigit
if [ ${#inp} -gt 7 ]; then
inp=${inp: $((7-${#inp}))}
else
unset inp
fi
done
print_result
Я проверил с параметром busybox в моемUbuntu и увидел, что он поддерживает 64-битную арифметику, поэтому мне нужен 32-битный лимб, чтобы избежать переполнения при умножении.Я выбрал выходную базу как 1 000 000 000
, потому что это наибольшая степень 10, которая может быть представлена в 32-битном int.Тогда входная база должна быть меньше, чем база (требуется меньше обработки переноса), поэтому я выбираю 0x10000000, наибольшую мощность 16, которая меньше, чем 1000000000
Конечно, если ваш занятый ящик настолько поврежден, что он не 't поддерживает 64-битное int, тогда вы должны использовать базу 0x1000 и обрабатывать 3 шестнадцатеричные цифры сразу
Подтверждение с помощью bc, результат всегда один и тот же
$ v=15ABC12345AFDA325; busybox sh ./hex2dec.sh $v; echo "ibase=16; $v" | bc
24984864848818840357
24984864848818840357
$ v=2B37340113436BA5C23513A1231111C; busybox sh ./hex2dec.sh $v; echo "ibase=16; $v" | bc
3590214682278754501437472025955340572
3590214682278754501437472025955340572
$ v=60431BCD73610ADF2B37340113436BA5C23513A12311111111111;\
> busybox sh ./hex2dec.sh $v; echo "ibase=16; $v" | bc
2474996796503602902399592755755761709869730986038055786310078737
2474996796503602902399592755755761709869730986038055786310078737