Bash String with HEX to DECIMAL

I have struggled with some similar problems already I believe discussed but really I do not get the way out: What if your file has data for example as below (hex values can be anything but fixed to 1 byte) and you want to convert each hex to decimal and keep it as in below format so instead of hex you display decimal and still comma separated? - Please note number of data lines is not fixed it can by any number.

Example 1:

0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51 0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0 0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51 0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0 0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51 0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0 

Example 2: same as above but lets say it has data name in front:

X:0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51 Y:0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a Z:0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0 X:0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51 Y:0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a Z:0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0 X:0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51 Y:0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a Z:0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0 

So I have spent some time to understand the bash grep, sed etc.. as I am completely not Software guy I manage to play and filter out some text files in the correct format next I would like to understand how to operate on strings with more than one value and above examples will really help . Thanks in advance.

I did try printf, echo and I am able to use them when each string in text file has one value for example : if I use data file which has data

0xff 0xff 0xff 0xff 0xff . . . . . . .. 0xff 

all works fine and can be converted with printf function: this was found in other thread:

while read line do printf '%d' $line done < file 

Above works great for the one hex value in line: Can this be adjusted to work with my examples or there is any better way?

1

6 Answers

You can use (GNU) Awk and it's strtonum function, which converts strings that start with 0x to decimal numbers.

  • Just splitting at , and starting from column 1:
awk 'BEGIN {FS=OFS=","} {for (i=1;i<=NF;i++) $i=strtonum($i); print}' file.txt 
81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 
  • Splitting at , or : and starting from column 2:
awk 'BEGIN {FS=",|:"; OFS=","} {for (i=2;i<=NF;i++) $i=strtonum($i); print}' file.txt 
X,81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y,90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z,240,240,240,240,240,240,240,240,240,240,240,240,240,240 X,81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y,90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z,240,240,240,240,240,240,240,240,240,240,240,240,240,240 X,81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y,90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z,240,240,240,240,240,240,240,240,240,240,240,240,240,240 
  • This way, however, also the : will be converted to ,. If this is not desired, you could print the columns 1, 2, and the rest separately:
awk 'BEGIN {FS=",|:"} {printf "%s:%s", $1, strtonum($2); for (i=3;i<=NF;i++) printf ",%s", strtonum($i); printf "\n"}' file.txt 
X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 
1

A generalized awk solution -

awk -F'[:,]' -v OFS=, '{for(i=1;i<=NF;i++){if($i~/^0x/){$i=strtonum($i)}}if($1!~/^[0-9]/)sub(",",":");print}' file 

Saved to a file, run with each of your example sets:

$: cat x2d #! /usr/bin/awk -f BEGIN{ FS="[:,]"; OFS="," } { for ( i=1; i<=NF; i++ ) { if ( $i ~ /^0x/ ) { $i=strtonum($i) } } if ( $1 !~ /^[0-9]/ ) sub(",",":"); print; } $: ./x2d withName X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 $: ./x2d noName 81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 

Just for the practice, the same in basic bash.

$: cat x2d #! /bin/bash while IFS=:, read -ra field do for f in "${field[@]}" do case $f in 0x*) printf %d, $f;; *) printf %s: $f;; esac done printf $'\n' done < $1 $: ./x2d withName X:81,81,81,81,81,81,81,81,81,81,81,81,81,81, Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90, Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240, X:81,81,81,81,81,81,81,81,81,81,81,81,81,81, Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90, Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240, X:81,81,81,81,81,81,81,81,81,81,81,81,81,81, Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90, Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240, $: ./x2d noName 81,81,81,81,81,81,81,81,81,81,81,81,81,81, 90,90,90,90,90,90,90,90,90,90,90,90,90,90, 240,240,240,240,240,240,240,240,240,240,240,240,240,240, 81,81,81,81,81,81,81,81,81,81,81,81,81,81, 90,90,90,90,90,90,90,90,90,90,90,90,90,90, 240,240,240,240,240,240,240,240,240,240,240,240,240,240, 81,81,81,81,81,81,81,81,81,81,81,81,81,81, 90,90,90,90,90,90,90,90,90,90,90,90,90,90, 240,240,240,240,240,240,240,240,240,240,240,240,240,240, 

For data sets this small, the difference is irrelevant.
If the inputs are big, the awk will be faster.

4

I'd use perl. For your first case:

$ perl -F, -lane 'print join(",", map(hex, @F))' ex1.txt 81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 

and the second, with the prefix:

$ perl -F'[:,]' -lane 'print $F[0], ":", join(",", map(hex, @F[1..$#F]))' ex2.txt X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 
2

A straightforward Bash solution would be to just let Bash parse integers like it normally does. For Example 2:

to_dec() { local -r IFS=,: local -a line local -ai nline while read -ra line; do nline=("${line[@]:1}") printf '%s:%s\n' "${line[0]}" "${nline[*]}" done } 

Then to_dec < file gives the expected output for Example 2. A to_dec for Example 1 will be much simpler, because there’s no need for an auxiliary array in that case; just read the numbers:

to_dec() { local -r IFS=, local -ai nline while read -ra nline; do printf '%s\n' "${nline[*]}" done } 

As for the math operations for Example 2, requested in a comment, in pure Bash one could create functions named after the line prefixes and then execute them on the arrays. In any case, it won’t be “efficient”; it’s Bash. (Also, presumably, Bash has integer division only; to circumvent that one could use a fixed-point representation of some sort, but that’s way too hacky in Bash.)

#!/bin/bash X() { local -n a="$1"; local -i i for i in "${!a[@]}"; do ((a[i] /= 2)); done; } Y() { local -n a="$1"; local -i i for i in "${!a[@]}"; do ((a[i] *= 2)); done; } Z() { local -n a="$1"; local -i i for i in "${!a[@]}"; do ((a[i] += 2)); done; } to_dec() { local -r IFS=,: local -a line local -ai nline while read -ra line; do nline=("${line[@]:1}") "${line[0]}" 'nline' # transform the array printf '%s:%s\n' "${line[0]}" "${nline[*]}" done } to_dec # takes the script's default stdin / stdout 

For a bit more efficiency and floating-point arithmetics, awk can help:

#!/usr/bin/awk -f BEGIN { FS = "[:,]" } function X(numbers) { for (i in numbers) numbers[i] /= 2 } function Y(numbers) { for (i in numbers) numbers[i] *= 2 } function Z(numbers) { for (i in numbers) numbers[i] += 2 } { for (i = 2; i <= NF; ++i) numbers[i] = strtonum($i) f = $1 @f(numbers) output = $1 ":" numbers[2] for (i = 3; i <= NF; ++i) output = output "," numbers[i] print output } 

For large data processing, a proper implementation in a more low-level programming language would be more efficient.

5

GNU AWK

awk -F'[,:]' -v OFS=, ' /:/{ withName=1 } { for(i=1;i<=NF;i++) $i=(strtonum($i) ? strtonum($i) : $i) } withName{ sub(/,/,":") withName=0 }1 ' file1 file2 81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 
echo '0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51 0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0 0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51 0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0 0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51 0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a 0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0' | 

gawk -nb '$_ += ORS = RT' RS='\n|,' 

81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 81,81,81,81,81,81,81,81,81,81,81,81,81,81 90,90,90,90,90,90,90,90,90,90,90,90,90,90 240,240,240,240,240,240,240,240,240,240,240,240,240,240 
 gawk -nMb '$++NF = +$_' OFS='\f' 

0x5151515151515151515151515151 1649317825652239364356745892811089 0x5a5a5a5a5a5a5a5a5a5a5a5a5a5a 1832575361835821515951939880901210 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0 4886867631562190709205173015736560 0x5151515151515151515151515151 1649317825652239364356745892811089 0x5a5a5a5a5a5a5a5a5a5a5a5a5a5a 1832575361835821515951939880901210 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0 4886867631562190709205173015736560 0x5151515151515151515151515151 1649317825652239364356745892811089 0x5a5a5a5a5a5a5a5a5a5a5a5a5a5a 1832575361835821515951939880901210 0xf0f0f0f0f0f0f0f0f0f0f0f0f0f0 4886867631562190709205173015736560 
echo ' X:0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51 Y:0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a Z:0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0 X:0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51 Y:0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a Z:0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0 X:0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51,0x51 Y:0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a,0x5a Z:0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0,0xf0' | 

gawk -nb 'NF == +(ORS = RT) || $_ += _' RS='\n?[A-Z]:|[\n,]' 

X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 X:81,81,81,81,81,81,81,81,81,81,81,81,81,81 Y:90,90,90,90,90,90,90,90,90,90,90,90,90,90 Z:240,240,240,240,240,240,240,240,240,240,240,240,240,240 

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

You Might Also Like