'Shell Script to convert hexadecimal values in a file to decimal
I have a file named as text containing all hexadecimal numbers. I wrote the following code to convert those values to decimal:
for line in `cat text`; do
arp=$(echo "ibase=16; $line" | bc);echo $arp
done
But it's giving me the following error:
(standard_in) 1: syntax error
My input file contains one column of hexadecimal values, e.g.
428a2f98
71374491
b5c0fbcf
Solution 1:[1]
It's not bash that produces the error, it's bc which requires hexadecimal numbers to use uppercase letters while your input file uses lowercase letters.
If you use bash 4 you can use ${foo^^} to expand $foo to uppercase:
bc <<< "ibase=16; ${line^^}"
or you can use tr:
bc <<< "ibase=16; $(tr '[:lower:]' '[:upper:]' <<< "${line}")"
Solution 2:[2]
If your hex numbers are in the following form:
0x1
0x2
0x3
0x4
0x5
0x6
0x7
0x8
0x9
0xA
0xB
0xC
0xD
0xE
0xF
I mean prefixed with 0x you can use:
while read line
do
printf '%d\n' $line
done < text
Ouput:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Solution 3:[3]
Although this question is answered, when you don't know exactly the position of numbers, this sed oneliner can help other people looking for help, and only finding this question on the net:
$ cat file | sed 's/^/echo "/;s/(0x\(..\))/(\$(echo "ibase=16;\U\1\E"\|bc))/g;s/$/"/e'
Explaining:
s/^/echo "/addsecho "at starts/(0x\(..\))/(\$(echo "ibase=16;\U\1\E"\|bc))/gneeds further explaining:- find
(0x..)pattern. You can change to^........in your case - creates a subshell
$()where hex pattern\1is procesed by bc\Uis used to uppercase hex digits.\Estops uppercasing.
- find
s/$/"/eadds"at end and process line with sh
You can test it on xmodmap output, which output hexadecimal characters for modifiers, while xev shows modifiers as decimals.
Hope it becomes handy in other use cases
Solution 4:[4]
while read line
do
arp=$(echo "ibase=16; $line" | bc)
echo $arp
done < in.txt
For in.txt
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F
A0
A1
FF
This prints:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
160
161
255
Solution 5:[5]
This answer is based on albfan's answer and posted for the same reason (other people looking for help, and only finding this question on the net).
I am expanding on it to allow for replacements in files that contain backslashes, backticks, double quotes and dollar signs.
This way it is also much safer to use on unknown inputs, preventing code injection.
#!/bin/bash
cat $1 | sed -r 's/\\/\\\\/g;s/`/\\`/g;s/"/\\"/g;s/\$/\\$/g;s/0x([0-9A-Fa-f]+)/\$(echo "ibase=16;\U\1\E" \| bc)/g;s/^/echo "/;s/$/"/e'
To answer the original question, the 0x needs to be removed:
#!/bin/bash
cat $1 | sed -r 's/\\/\\\\/g;s/`/\\`/g;s/"/\\"/g;s/\$/\\$/g;s/([0-9A-Fa-f]+)/\$(echo "ibase=16;\U\1\E" \| bc)/g;s/^/echo "/;s/$/"/e'
Simple demonstration:
Input line:
console.log("This is a number: 0xffffff")
Intermediate form:
echo "console.log(\"This is a number: $(echo "ibase=16;FFFFFF" | bc)\")"
Output line:
console.log("This is a number: 16777215")
Usage:
Save as hex-to-decimal.sh, chmod 555 hex-to-decimal.sh to make it executable and uneditable and run with ./hex-to-decimal.sh inputfilename > outputfilename
Or just run as a one-liner with cat inputfilename | sed -r 's/\\/\\\\/g;s/`/\\`/g;s/"/\\"/g;s/\$/\\$/g;s/0x([0-9A-Fa-f]+)/\$(echo "ibase=16;\U\1\E" \| bc)/g;s/^/echo "/;s/$/"/e' > outputfilename
Explanation:
cat $1
- List the contents of the input file line by line.
| sed -r
- Apply a bunch of transformations (in this case substitutions and one execution) to each line of text and print the result to stdout.
s/\\/\\\\/g;
- Place a backslash before every backslash in this line.
s/`/\\`/g;
s/"/\\"/g;
s/\$/\\$/g;
- Do the same for backticks, double quotes and dollar signs.
s/0x([0-9A-Fa-f]+)
- Substitute anything that starts with
0xand is followed by a hexadecimal number. - For the original question version,
0xis omitted and it matches anything that is a valid hexadecimal number. For example causingfridgeto be replaced with15ri13g14.
/\$(echo "ibase=16;\U\1\E" \| bc)/g;
- Replace it with
$(echo "ibase=16;+ the first group capitalized (the number without0x) +" | bc) \$(echo "ibase=16;\U\1\E" \| bc)$(<command>)- starts a child bash instance that executes the command and resumes execution with it being replaced by the output of the child (has no effect from within single quote (
'') strings)
- starts a child bash instance that executes the command and resumes execution with it being replaced by the output of the child (has no effect from within single quote (
\U\1\E\Umakes what comes after uppercase,\1is the first group (which was[0-9A-Fa-f]+),\Emakes the uppercasing not apply to what comes after it. In other words; the hexadecimal number, made uppercase.
echo "ibase=16;<hexadecimal number>" | bc- Convert a hexadecimal number (input base = 16) to decimal (default output type) with the
bccommand.
- Convert a hexadecimal number (input base = 16) to decimal (default output type) with the
s/^/echo "/;
- Place
echo "at the start of the line.
s/$/"/e
- Place
"at the end of the line and execute the line.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | a5hk |
| Solution 3 | albfan |
| Solution 4 | |
| Solution 5 |
