Generating hex numbers of a certain range
Solution 1
Another Attempt (still needs seq but not bc) - works in cygwin and linux:
seq 0 255 | while read n; do printf "%04X;" $n; done
The inputs are decimal, but the output is capitalised hex and you can change the digits to show by updating the "04" part of the printf format string
Solution 2
Here's a script that's portable (any POSIX shell will do), should work blazingly fast (no forks to nawk, bc, etc...) and adheres to the poster's spec.
#!/bin/sh
#
# hexcount - POSIX Shell script to count from $1 to $2 in hex,
# separated by ";" and with the precision set to the
# maximum digits of $1 and $2.
# Usage: hexcount lo hi
# Example: hexcount FFF 1200
from=$1 to=$2
if test "${#from}" -gt "${#to}"; then
format="%0${#from}X;"
else
format="%0${#to}X;"
fi
from=$(printf '%d' "0x$from") to=$(printf '%d' "0x$to")
while test "$from" -le "$to"; do
printf "$format" "$from"
from=$((from+1))
done
printf '\n'
Ain't printf cool?
Solution 3
I just like to use simple for loops. You can add as many nested loops as you like for each digit:
for i in {0..9} {a..f}; do for x in {0..9} {a..f}; do for y in {0..9} {a..f}; do printf "$i$x$y;"; done; done; done
Output should look like this:
000;001;002;003;004;005;006;007;008;009;00a;00b;00c;00d;00e;00f;010;011;012;013;014;015;016;017;018...ff8;ff9;ffa;ffb;ffc;ffd;ffe;fff;
That was a one-liner, but let me give it some structure:
for i in {0..9} {a..f}
do
for x in {0..9} {a..f}
do
for y in {0..9} {a..f}
do
printf "$i$x$y;"
done
done
done
Solution 4
I know I'm really late to this party, but here's my uber bash solution. You iterate the value range in decimal and use printf.
#!/bin/bash
for i in $( seq 0 100 ); do
printf "%03X;" $i >> test.txt
done
I think it makes sense and fits the brief of the formatting + being simple. Worth noting that you need to adjust the "3" in "%03X" to match the field width you want to print.
Update: you can always over-engineer a thing.
#!/bin/sh
# test that there are 2 command line parameters
if [ $# -eq 2 ]; then
# test that the first is numeric
if [ -n "$1" ] && [ "$1" -eq "$1" ] 2> /dev/null; then
# test that the 2nd is numeric
if [ -n "$2" ] && [ "$2" -eq "$2" ] 2> /dev/null; then
# test that the 2nd is >= the 1st
if [ "$2" -ge "$1" ]; then
# determine the field width of the higher number
ENDHEX=$( printf "%X" $2 );
ENDWIDTH=${#ENDHEX}
# Generate the hex sequence
for i in $( seq $1 $2 ); do
printf "%0${ENDWIDTH}X;" $i
done
exit 0
fi
fi
fi
fi
echo "hex script thing"
echo "usage: script <min> <max>"
echo "script will then print the sequence of numbers in hex, delimited by semicolons"
echo "to output to file use: script <min> <max> >> output.txt"
exit 1
update to the update: you can always code golf a thing too. printf fans know what's up.
#!/bin/sh
printf "%02X;" {0..100}
Solution 5
Here's an implementation tested in KSH on OpenBSD (should work with any Bourne shell, doesn't rely on bash
features).
#!/bin/sh
START=$1
END=$2
function h2d {
echo "ibase=16; $@"|bc
}
function d2h {
echo "obase=16; $@"|bc
}
# convert to decimal
START_DEC=`h2d ${START}`
END_DEC=`h2d ${END}`
# find length of last hex number
LENGTH=`expr ${#END} - 1`
for i in `nawk "BEGIN{ for(i=${START_DEC};i<=${END_DEC};i++) print i}"`
do
# convert output to hex
OUTPUT=`d2h ${i}`
# calculate output length
OUTPUT_LENGTH=`expr ${#OUTPUT}`
# calculate required padding
PAD_LENGTH=`expr ${LENGTH} - ${OUTPUT_LENGTH}`
# output padding
for j in `nawk "BEGIN{ for(j=0;j<=${PAD_LENGTH};j++) print j}"`
do
echo -n 0
done
# output number
echo -n ${OUTPUT}\;
done
# for the newline
echo
Sample output:
$ ./hex-range.sh 91 FF
91;92;93;94;95;96;97;98;99;9A;9B;9C;9D;9E;9F;A0;A1;A2;A3;A4;A5;A6;A7;A8;A9;AA;AB;AC;AD;AE;AF;B0;B1;B2;B3;B4;B5;B6;B7;B8;B9;BA;BB;BC;BD;BE;BF;C0;C1;C2;C3;C4;C5;C6;C7;C8;C9;CA;CB;CC;CD;CE;CF;D0;D1;D2;D3;D4;D5;D6;D7;D8;D9;DA;DB;DC;DD;DE;DF;E0;E1;E2;E3;E4;E5;E6;E7;E8;E9;EA;EB;EC;ED;EE;EF;F0;F1;F2;F3;F4;F5;F6;F7;F8;F9;FA;FB;FC;FD;FE;FF;
Regarding output to a text file, just use the shell redirection operator:
$./hex-range.sh 91 FF > output.txt
Related videos on Youtube
Dave
Updated on May 27, 2022Comments
-
Dave almost 2 years
I need a simple way to generate a .txt file with a list of semi-colon delimited hex numbers between (inclusive) a certain start and finish value.
For example:
If I enter
0
andFFF
it would pad the output with zeroes to the largest number:000;001;002;003;004;005;006;007;008;009;00A;00B;00C;00D;00E;00F;010;011;....FFF;
If I enter
FFF
and1200
it would output those values...etc:0FFF;1000;1001;1002;.....1200;
Any suggestions? I'm not a programmer so the best, simplest way to do this is way beyond me.
-
chuckx about 13 yearsWhile my version isn't optimized for brevity or speed, it does meet all of the stated requirements. In particular, the dynamic padding based on the input, which isn't accounted for here (and is responsible for the length of the script).
-
Jens over 12 yearsThis is not a Shell script.
for (( ... ))
is a non-portable shell extension (certainly not POSIX) and deviates from your original spec in that now the output is 0-padded to 5 digits no matter what the start and end numbers are, and prefixed with&#
. It would have been better to state your actual requirements in the first place... -
Jens almost 12 yearsNot quite bournish: The
function
reserved word is unknown to ancient sh. Useh2d () { ...; }
instead. -
Charles Duffy about 7 yearsNot just "ancient" sh -- it's not compliant with modern POSIX sh either.
-
Charles Duffy about 7 yearsAlso,
echo -n
isn't specified by POSIX (or, rather, it's specified to have undefined behavior);printf
is preferred instead. And$(( ))
is both POSIX-defined and far more efficient than forking a subshell to runexpr
. -
Charles Duffy about 7 yearsnod -- if you're going to use C-style
for
loops, use a shebang for a shell that's guaranteed to support them -- so not#!/bin/sh
(which guarantees only POSIX compliance but no extensions), but#!/bin/bash
or similar. -
Charles Duffy about 7 years
seq
is not a POSIX-specified tool, and thus not guaranteed to be present and available. -
Charles Duffy about 7 yearsAt present, this is faster to execute in ksh93 (which recognizes
var=$(printf ...)
and optimizes it to avoid a fork) than bash (which doesn't); but that's not easily fixed without reducing portability (as by using bash-only syntax such asprintf -v from '%d' "0x$from"
). It's an outstanding answer nonetheless (and those lone forks, when they do occur, are outside a loop, making them relatively inconsequential) -- and hopefully bash will improve to execute it as efficiently as ksh93 does in the future. -
Jens about 5 yearsThis always starts at zero. The question also mentions a non-zero starting number.
-
Fernando Cordeiro over 3 yearsJust for kicks, "because"....
seq $(printf %d 0xA000) $(printf %d 0xA0FF) | xargs -- printf "%04x;"
in Bash. If you do use Fish like me, it's actuallyseq (printf %d 0xA000) (printf %d 0xA0FF) | xargs -- printf "%04x;"
'cause fish people seem to dislike Dollars for some reason... ;D Maybe there's a better way to deal with bash expansions, my limit on that front was this:bash -c "echo {$(printf %d 0xA000)..$(printf %d 0xA0FF)}|tr ' ' '\n' | xargs -- printf %04x\\\n | paste -sd ';' -"