I'm new to BASH scripting, but I need a quick way to convert a signed hexadecimal to signed decimal. For instance FF should be -1 not 256. The msbit should be sign bit.
so far I have.. and I need and signed decimal from a signed hex word.
#! /bin/sh
ADDR=`echo $1 | tr a-z A-Z`
ADDR2=`echo "ibase=16; obase=10; $ADDR+1" | bc`
VARmsb=`./mpu-6050-getbyte $ADDR;`
VARlsb=`./mpu-6050-getbyte $ADDR2;`
echo $VARmsb$VARlsb
Probably not canonical, but this function should do it:
function conv() {
res=$(printf "%d" "0x$1")
(( res > 127 )) && (( res -= 256 ))
echo $res
}
For instance:
conv 1
conv 0A
conv 7F
conv 80
conv FF
1
10
127
-128
-1
This should do the trick
signed_dec=$(printf "%d" $signed_hex)
If you need the result to fit inside a certain range then you can apply a modulo operator via bash arithmetic e.g:
signed_dec=$(($(printf "%d" 0xFAB) % 256))
Related
This question already has an answer here:
Convert a character from and to its decimal, binary, octal, or hexadecimal representations in BASH / Shell
(1 answer)
Closed 5 months ago.
The community reviewed whether to reopen this question 5 months ago and left it closed:
Original close reason(s) were not resolved
How to convert binary, octal, decimal and hexadecimal values between each other in BASH / Shell ?
Please provide working examples
Convert binary, octal, decimal and hexadecimal values between each other in BASH with bc and printf
Some relevant Q&A:
Understand "ibase" and "obase" in case of conversions with bc?
TL;DR: ibase and obase params order matters, but not always. Hex values must be in UPPERCASE.
Convert a character from and to its decimal, binary, octal, or hexadecimal representations in BASH / Shell
Convert decimal to hexadecimal in BASH. Examples
echo "obase=16; 255" | bc
FF
echo "ibase=10; obase=16; 255" | bc
FF
echo "obase=16; ibase=10; 255" | bc
FF
printf "%x %08x %#x %#08x %X %08X %#X %#08X" {255,255,255,255,255,255,255,255}
ff 000000ff 0xff 0x0000ff FF 000000FF 0XFF 0X0000FF
Convert hexadecimal to decimal in BASH. Examples
echo "ibase=16; FF" | bc
255
echo "ibase=16; obase=10; FF" | bc # wrong
FF
echo "obase=10; ibase=16; FF" | bc
255
printf "%d " {0XFF,0X000000FF,0xff,0x000000ff}
255 255 255 255
echo $((16#FF))
255
Convert hexadecimal to binary in BASH. Examples
echo "ibase=16; obase=2; FF" | bc
11111111
echo "obase=2; ibase=16; FF" | bc
11111111
Convert binary to hexadecimal in BASH. Examples
echo "obase=16; ibase=2; 11111111" | bc
FF
echo "ibase=2; obase=16; 11111111" | bc # wrong
100110
Convert hexadecimal to octal in BASH. Examples
echo "ibase=16; obase=8; FF" | bc
377
echo "obase=8; ibase=16; FF" | bc
377
printf "%o " {0XFF,0X000000FF,0xff,0x000000ff}
377 377 377 377
printf "%#o " {0XFF,0X000000FF,0xff,0x000000ff}
0377 0377 0377 0377
printf "%08o " {0XFF,0X000000FF,0xff,0x000000ff}
00000377 00000377 00000377 00000377
Convert octal to hexadecimal in BASH. Examples
echo "obase=16; ibase=8; 377" | bc
FF
echo "ibase=8; obase=16; 377" | bc # wrong
143
printf "%x %08x %#x %#08x %X %08X %#X %#08X" {0377,0377,0377,0377,0377,0377,0377,0377}
ff 000000ff 0xff 0x0000ff FF 000000FF 0XFF 0X0000FF
I'm trying to read values in i2c addresses from my raspberry pi. Through the use of i2ctools, I can take an address and store it in a variable.
reg_state=$(i2cget -y 1 0x20 0x09)
echo "$reg_state"
:~/ $ 0x0a
However, although the address reads in properly, reg_state will keep the hex extension of "0x0" and this hinders doing operations. Say 0x0a was in the register before, and I want to add "1" to that value, the operation won't complete. I think this has to do with the way I'm trying to achieve my goal. Right now my code looks like this:
7 addition(){
8 reg_state=$(i2cget -y 1 0x20 0x09)
9 i=$(echo "obase=10; $reg_state"| bc)
10 write=$(i+adj )
}
...
25
26#Main Shell Script
27
28 op=$1
29 adj=$2
30 if [ $1 -gt 0 ]
31 then
32 addition
33 fi
What I'm attempting to do is read in the value of the register at an address, convert it to a decimal number and then add it with any number I want. However, I noticed that I am unable to use echo "obase=10; $reg_state | bc" because of the presence of '0x'. When converting hex numbers without the extension everything works fine, and they can be added like normal.
That being said, is there anyway I can get rid of the '0x' part and just have what's left so I can do my arithmetic in peace?
You shouldn't need bc to do integer math, even on hex numbers. Just use shell arithmetic expansion. echo "$(( 0xA + 1))" will display 11, for example. And if you need the result in hex:
printf "0x%X" $(( 0xA + 1))
will print 0xB.
I am new to shell script
I Have tried to multiply two hex numbers in shell script in the following manner.
initial= expr 0x10000 \* 0x22
echo $initial
While running the script,The following error is seen.
expr: non-numeric argument
Can someone point out what might the mistake?
No need to expr, use $(( )) just like this:
$ echo $((0x10000 * 0x22))
2228224
Or you can use bc like this, indicating input is hex (ibase) and desired output also in hex (obase) (as Adobe's deleted answer states):
$ echo "ibase=16; obase=16; 10000*22" | bc
09 11 05 16 20
$ echo "ibase=16; 10000*22" | bc
2228224
I am trying to dump the decimal integer values from one file in a hex format.
I do have a file with integer values in decimal.
$ more test.dat_trim
2 9
0 -11
7 -17
14 -1
I am trying to print this integer in hex. I know also that the integer values are small enough to fit on 2 bytes. I want the output to be on 2 bytes. But then when i am trying:
declare -i i;for i in $(<test.dat_trim);do printf "%.2x\n" $i; done;
02
09
00
fffffffffffffff5
07
ffffffffffffffef
0e
ffffffffffffffff
Basically printf "%.2x\n" it is only working for positive number. How can i make it work for negative also?
Just to clarify what i am expecting: The result should be like this:
02
09
00
f5
07
ef
0e
ff
meaning that i want for the negative values to be sign extended only on 1 byte.
Printing signed hex values is uncommon, so there is no conversion specifier providing this.
You could use the following work around:
for i in $(<test.dat_trim); do
if [ $i -ge 0 ]; then
printf " 0x%02x\n" $i;
else
printf "%c0x%02x\n" '-' $[$i * -1];
fi
done;
Referrig the update to the question:
Just replace this line
printf "%c0x%02x\n" '-' $[$i * -1];
with this
printf " 0x%02x\n" $[256 + $i];
This however, only works for the numbers >= -256.
It can be done in awk, that handles negative numbers also:
awk '{printf "0x%x%s0x%x\n", $1, OFS, $2}' OFS='\t' file
0x2 0x9
0x0 0xfffffff5
0x7 0xffffffef
0xe 0xffffffff
Kinda silly but what the heck:
xargs -a test.dat_trim bash -c 'printf %.2s\\n $(printf %02x\\n $* | rev) | rev' _
Have you tried printf("%04x\n",i)?
I have a file with binary data and I need to replace a few bytes in a certain position. I've come up with the following to direct bash to the offset and show me that it found the place I want:
dd bs=1 if=file iseek=24 conv=block cbs=2 | hexdump
Now, to use "file" as the output:
echo anInteger | dd bs=1 of=hextest.txt oseek=24 conv=block cbs=2
This seems to work just fine, I can review the changes made in a hex editor. Problem is, "anInteger" will be written as the ASCII representation of that integer (which makes sense) but I need to write the binary representation.
I want to use bash for this and the script should run on as many systems as possible (I don't know if the target system will have python or whatever installed).
How do I tell the command to convert the input to binary (possibly from a hex)?
printf is more portable than echo. This function takes a decimal integer and outputs a byte with that value:
echobyte () {
if (( $1 >= 0 && $1 <= 255 ))
then
printf "\\x$(printf "%x" $1)"
else
printf "Invalid value\n" >&2
return 1
fi
}
$ echobyte 97
a
$ for i in {0..15}; do echobyte $i; done | hd
00000000 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f |................|
00000010
You can use echo to emit specific bytes using hex or octal. For example:
echo -n -e \\x30
will print ascii 0 (0x30)
(-n remove trailing newline)
xxd is the better way. xxd -r infile outfile will take ascii hex-value in infile to patch outfile, and you can specify the specific position in infile by this: 1FE:55AA
Worked like a treat. I used the following code to replace 4 bytes at byte 24 in little endian with two integers (1032 and 1920). The code does not truncate the file.
echo -e \\x08\\x04\\x80\\x07 | dd of=<file> obs=1 oseek=24 conv=block,notrunc cbs=4
Thanks again.
I have a function to do this:
# number representation from 0 to 255 (one char long)
function chr() { printf "\\$(printf '%03o' "$1")" ; return 0 ; }
# from 0 to 65535 (two char long)
function word_litleendian() { chr $(($1 / 256)) ; chr $(($1 % 256)) ; return 0 ; }
function word_bigendian() { chr $(($1 % 256)) ; chr $(($1 / 256)) ; return 0 ; }
# from 0 to 4294967295 (four char long)
function dword_litleendian() { word_lilteendian $(($1 / 65536)) ; word_litleendian $(($1 % 65536)) ; return 0 ; }
function dword_bigendian() { word_bigendian $(($1 / 65536)) ; word_bigendian $(($1 % 65536)) ; return 0 ; }
You can use piping or redirection to catch the result.
If you're willing to rely on bc (which is fairly common)
echo -e "ibase=16\n obase=2 \n A1" | bc -q
might help.
With bash, "printf" has the "-v" option, and all shell has logical operators.
So here is simplier form in bash :
int2bin() {
local i=$1
local f
printf -v f '\\x%02x\\x%02x\\x%02x\\x%02x' $((i&255)) $((i >> 8 & 255)) $((i >> 16 & 255)) $((i >> 24 & 255))
printf "$f"
}
You might put the desired input into a file and use the "if=" option to dd to insert exactly the input you desire.
In my case, I needed to go from a decimal numeric argument to the actual unsigned 16-bit big endian value. This is probably not the most efficient way, but it works:
# $1 is whatever number (0 to 65535) the caller specifies
DECVAL=$1
HEXSTR=`printf "%04x" "$DECVAL"`
BYTEONE=`echo -n "$HEXSTR" | cut -c 1-2`
BYTETWO=`echo -n "$HEXSTR" | cut -c 3-4`
echo -ne "\x$BYTEONE\x$BYTETWO" | dd of="$FILENAME" bs=1 seek=$((0xdeadbeef)) conv=notrunc