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?
16 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 column1:
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 column2:
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 columns1,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 1A 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.
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 2A 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.
5GNU 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