Skip to content

Bash – Insert number variables into command

I’ve been wanting to have a count-up timer overlaying a video but all I could find online for doing so was using Adobe After Effects which isn’t really an option as I’m on Linux and am unwilling to pay for it.

So, if I could get a series of images or a video, then it could be imported into Openshot to overlay a video quite nicely, but it’s been a struggle getting said series of images or video.

I’ve started work on a script to make the series of images but have no idea how to place variables into the command. Here’s the script in its current form:

for i in {0..18000000}
    echo "$i"
    convert -size 780x100 xc:#d3fff5ff -font Courier-New -pointsize 100 -fill black -draw "text 20,80 '000:00:00.00'" 0.png

What I would like is for 0.png to essentially be $i.png, so it increments up to 18,000,000. The time (shown as 000:00:00.00) I could probably work out for myself after knowing how to insert variables into commands. The time format is hours, minutes, seconds and frames.

Here’s an example for if I’ve poorly explained things (sorry for the spacing – only double new lines only work on here for some reason):

0.png = 000:00:00.00

1.png = 000:00:00.01

49.png = 000:00:00.49

50.png = 000:00:01.00

51.png = 000:00:01.01

18000000.png = 100:00:00.00



This kind of builds on chepner’s and choroba’s answers but also addresses how you get 18,000,000 images generated using all your CPU’s cores. I benchmarked 3 different methods on a decent spec iMac and achieved the following results – starting with the simplest and slowest and going from there to faster methods…

Method 1 – Plain Sequential bash script

for ((i=0; i<18000000; i++ )); do
   f=$(( i % 50 ))
   s=$(( (i/50) % 60 ))
   m=$(( (i/(50*60)) % 60 ))
   h=$(( (i/(50*60*60)) ))
   convert -size 780x100 xc:#d3fff5ff -font Courier -pointsize 100 -fill black -draw "text 20,80 '$(printf "%03d:%02d:%02d.%02d" $h $m $s $f)'" $i.png

This takes around 4.6 days to generate 18 million images.

Method 2 – GNU Parallel script


# Define a function for GNU Parallel to run
doit() {
   f=$(( i % 50 ))
   s=$(( (i/50) % 60 ))
   m=$(( (i/(50*60)) % 60 ))
   h=$(( (i/(50*60*60)) ))
   convert -size 780x100 xc:#d3fff5ff -font Courier -pointsize 100 -fill black -draw "text 20,80 '$(printf "%03d:%02d:%02d.%02d" $h $m $s $f)'" $i.png

# Export doit() function so GNU Parallel knows about it
export -f doit

ulimit -S -n 2048

for ((i=0;i<=18000000;i++)); do
   echo $i
done | parallel -k doit {1} 

This keeps all your CPU cores busy and takes around 32 hours to generate 18 million files. I think it’s a pretty good compromise compared to having to write code and compile and build it as in the next answer.

Method 3 – Magick++ program running 8 copies in parallel

#include <Magick++.h>

using namespace std;
using namespace Magick;

int main(int argc, char* argv[]){

   // Pick up parameters
      printf("ERROR: Please supply 2 arguments - start and end numbern");
   int start=atoi(argv[1]);
   int end  =atoi(argv[2]);


   // Create a basic template
   Image img(Geometry(780,100),Color("#d3fff5ff"));

   // Get cycling through the images
   for(int i=start;i<=end;i++){
      // Work out timestamp for this frame
      int f=i % 50;
      int s=(i/50) % 60;
      int m=(i/(50*60)) % 60;
      int h=(i/(50*60*60));
      char timestamp[100];

      // Copy the basic template image
      Image thisframe=img;
      char filename[100];

Here is how you run 8 copies in parallel:

./main 1 2250000 &
./main 2250001 4500000 &
./main 4500001 6750000 &
./main 6750001 9000000 &
./main 9000001 11250000 &
./main 11250001 13500000 &
./main 13500001 15750000 &
./main 15750001 18000000 &

This takes just under 9 hours to generate the 18 million files.

User contributions licensed under: CC BY-SA
5 People found this is helpful