Generating hex numbers of a certain range

14,384

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
Share:
14,384

Related videos on Youtube

Dave
Author by

Dave

Updated on May 27, 2022

Comments

  • Dave
    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 and FFF 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 and 1200 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
    chuckx about 13 years
    While 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
    Jens over 12 years
    This 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
    Jens almost 12 years
    Not quite bournish: The function reserved word is unknown to ancient sh. Use h2d () { ...; } instead.
  • Charles Duffy
    Charles Duffy about 7 years
    Not just "ancient" sh -- it's not compliant with modern POSIX sh either.
  • Charles Duffy
    Charles Duffy about 7 years
    Also, 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 run expr.
  • Charles Duffy
    Charles Duffy about 7 years
    nod -- 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
    Charles Duffy about 7 years
    seq is not a POSIX-specified tool, and thus not guaranteed to be present and available.
  • Charles Duffy
    Charles Duffy about 7 years
    At 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 as printf -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
    Jens about 5 years
    This always starts at zero. The question also mentions a non-zero starting number.
  • Fernando Cordeiro
    Fernando Cordeiro over 3 years
    Just for kicks, "because".... seq $(printf %d 0xA000) $(printf %d 0xA0FF) | xargs -- printf "%04x;" in Bash. If you do use Fish like me, it's actually seq (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 ';' -"