3

I am a complete newby to this but I'm trying to adapt someone else's code for a jail broken Kindle 3 (e-reader).

I am using the following code to generate a random(ish) number between 0 to X (where X is the number of images I have, currently 33) so that I can then display a random image every so many hours (currently every 2 hours) using cron to run the script.

#generate random number between my 33 images (compatible with busybox)
n=32
while [ $n -ge 32 ]; do
n=1$(</dev/urandom tr -dc 0-9 | dd bs=5 count=1 2>/dev/null)
n=$((n-100000))
done
ImageNumber=$n

#My images are numbered as follows - pic_0.png, pic_1.png, pic_3.png... etc
SelectedImage=$( find /mnt/us/timelit/images/pic_${ImageNumber}*.png)

# clear the screen
eips -c

# show that image
eips -g $SelectedImage

It is working however, there are a couple of things that needs to be solved.

1) I believe that there is a problem with the random number generator part of the script as it varies in time before the number is generated (see log below)

14:00:02: Script started
14:02:43: Random number is 2 
14:02:44: Image shown is pic_2.png 
15:00:02: Script started
15:00:19: Random number is 18 
15:00:20: Image shown is pic_18.png 
16:00:01: Script started
16:02:44: Random number is 10 
16:02:45: Image shown is pic_10.png  

2) Now and then the Kindle displays a blank screen rather than one of the images. I am guessing that there is an issue with the random number generated which means that a PNG with a matching number can't be found so the Kindle display is blank.

Any help is greatly appreciated.

New contributor
user395294 is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
1
#generate random number between my 33 images (compatible with busybox)
n=32
while [ $n -ge 32 ]; do
n=1$(</dev/urandom tr -dc 0-9 | dd bs=5 count=1 2>/dev/null)
n=$((n-100000))
done
ImageNumber=$n

So here you are generating a random number 00000-99999 and only terminate if it was lower than 32. Probability 32 out of 100'000. If the leading 0 is interpreted as octal number it might even cause invalid arithmetic expression problems. (Edit: Oh, I guess that's why you prepend the 1)

Either way that... takes a while to terminate. And raises the question why you don't just use $RANDOM%32. For smallish numbers, $RANDOM (which is limited to 0-32767) works fine.

If there is no $RANDOM in Kindle's busybox (possible if CONFIG_ASH_RANDOM_SUPPORT=n) you could just grab a single number with tr... or hexdump if it's available, then use that directly.

In any case, the while loop is very inefficient and pointless.

Example without loop:

random=1$(</dev/urandom tr -dc 0-9 | dd bs=5 count=1 2>/dev/null)
ImageNumber=$(($random % 32))

Still a bit inefficient since tr has to read a lot more random bytes than the 5 you want (10 out of 256 chance to read a digit).

With hexdump if available (reading a fixed 3 random bytes):

 random=0x$(hexdump -v -e '3/1 "%02x"' -n 3 /dev/urandom)
 ImageNumber=$(($random % 32))
#My images are numbered as follows - pic_0.png, pic_1.png, pic_3.png... etc
SelectedImage=$( find /mnt/us/timelit/images/pic_${ImageNumber}*.png)

If your ImageNumber is 1, then that expression translates to pic_1*.png which could be pic_1.png but also pic_10-19.png, pic-100-199.png and so on. If there are no extra characters in your filenames, get rid of the *. Otherwise you need another separator like pic_${ImageNumber}_*.png (to allow filenames like pic_1_spaceship.png).

Either way you can skip the find and juse use the expression directly:

eips -g /mnt/us/timelit/images/pic_${ImageNumber}.png
  • Thanks for the help so far. You are correct I cannot use $RANDOM. I think you're correct about the reason the image didn't display. I checked my log again and noticed that although the number 1 had been selected pic_1.png was not being displayed. There are no extra characters after the number so i will remove the * as you have suggested. Would it work and be better if i change 100000 to the maximum number of image files i have? I am not familiar with how to use tr to remove characters (i would probably need a three digit number and rename my images to pic_001.png, pic_002.png etc. – user395294 Feb 13 at 18:54
  • @user395294 added two examples – frostschutz Feb 13 at 19:02
  • AWESOME! @frostschutz I totally appreciate your help. I used ' random=0x$(hexdump -v -e '3/1 "%02x"' -n 3 /dev/urandom) ImageNumber=$(($random % 32))' which worked and the log shows that there is now only 1 second between the random number being generated and the image being shown. Would i be correct in saying that if i add more images i only have to change the 32 to the number of images for that to continue to work? – user395294 Feb 13 at 19:24
1

See frostschutz's answer for why your code doesn't work well. I'm going to give different solutions.

If your BusyBox environment includes awk, an easier way to generate a random number that's good enough for this application is

awk -v n=32 'BEGIN {srand(); print int(rand() * n); exit}'

srand() is solely based on the current time rounded to the nearest second, so there are many cases where it isn't appropriate (anything security-related, anything where there may be more than one random draw per second). But here it's good enough.

Rather than assign a number to each picture and hard-code the range of numbers, list the files and pick one at random. More precisely:

  • Set the positional arguments to the list of files.
  • Use awk to pick one positional argument at random.
  • Select that file name among the positional arguments.
set -- /mnt/us/timelit/images/pic*.png
i=$(awk -v n=$# 'BEGIN {srand(); print int(rand() * n ) + 1; exit}')
eval "SelectedImage=\${$i}"

eips -c
eips -g $SelectedImage

You can even let awk select the image file directly, although if there are a lot of files you might run into a command line length limit.

SelectedImage=$(awk '
    BEGIN {srand(); print ARGV[int(rand() * (length(ARGV)-1) + 1)]; exit}
  ' /mnt/us/timelit/images/pic*.png)

(Note that if you had a more complete environment with bash and GNU utilities, there'd be easier ways to do some things. My answer sticks to features that are available in every POSIX shell and in many BusyBox installations.)

  • Thanks for taking the time to offer other advice/solutions/improvements. My 50 year old brain is severly struggling with the steep learning curve! However even though @frostchutzs solution works for me, i will try your suggestions later in an effort to keep learning. Thanks – user395294 Feb 13 at 19:38

Your Answer

user395294 is a new contributor. Be nice, and check out our Code of Conduct.

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.