Batchfile: simple slideshow with custom image order

Last week a friend of mine wanted to have a image slideshow for a presentation TV screen for an exhibition. Besides of a large number of images that should be displayed in order he wanted to display his company logo image every second image, like:

  1. image 1
  2. company image
  3. image 2
  4. company image
  5. image 3
  6. company image

I wanted to do this without writing a program or installing Perl (his laptop is a Windows box) so I did this with a tiny Windows batch file:

setLocal EnableDelayedExpansion

SET count=1

FOR %%a IN (*.*) DO ( call :do "%%a" )

SET UQ1=%1
ren %UQ1% "%count%_!UQ1:"=!"
set /a count+=1
copy %FILLIMAGE% "%count%.jpg"
set /a count+=1
GOTO :eof

Given the batchfile slideshow.bat, the directory with a copy of all the source images in C:images and the company logo image in C:companyimage.jpg the call simply is:

slideshow.bat "C:images" "C:companyimage.jpg"

This will rename all the pictures found in C:images with a number in front of the original file name – with one number left out; like 1,3,5,7,etc. The company image is copied in between all these images as 2.jpg, 4.jpg, 6.jpg, etc. Now simply viewing the first image with Windows Picture Viewer and hitting the slideshow button will display the files by name which is exactly the desired display order.

Regarding the batchfile: it would be possible to execute multiple statements within the DO ( … ) but that way the counter variable did not work. I had to do a call to a method which then changes the counter. Another issue was the unquoting of the file name. The “%%a” supplies the image file name with double quotes to the %1 inside the :do function. As the final name should be counter + image-name without unquoting it would look like 1_”my image.jpg” which will do an error. The magick is the !VAR:”=! which unquotes the string in VAR.


Find UTF-8 byte order marks

In a templating application I just ran into ugly “” characters a the beginning of the text. This is caused by the byte order mark with the hex characters 0xEFBBBF. As it was not the only one file that contained the BOM I ran a search:

find . -iname '*.css' -o -iname '*.html' -o -iname '*.js' -o -iname '*.pm' -o -iname '*.pl' -o -iname '*.xml' | xargs grep -rl $'xEFxBBxBF'

To remove the BOM I followed the suggested way by using sed:

find . -iname '*.css' -o -iname '*.html' -o -iname '*.js' -o -iname '*.pm' -o -iname '*.pl' -o -iname '*.xml' -exec sed 's/^xEFxBBxBF//' -i.bak {} ; -exec rm {}.bak ;

Tada, no more ugly BOMs!

Optimize all PNG images recursively

Windows batch file syntax sometimes is a pain compared to UNIX bash/sh. Using optipng I wanted to shrink all the PNG images in a directory and inside its subdirectories as well. Notice that I had to write the command into a .bat batch file, it did not work directly from the command line:

FOR /F "tokens=*" %%G IN ('dir /s /b *.png') DO optipng -nc -nb -o7 -full %%G

The optipng params -nc and -nb prevent any color and color depth changes of the png files. Those may change the appearance, for example in the evil Internet Explorer. -o7 means the best and slowest optimization and -full does a full scan of the IDAT part. Using UNIX the same job may look like:

find . -name *.png | xargs optipng -nc -nb -o7 -full

Perl -e in Windows batch and Linux shell scripts

Recently I had to extract a partial string from a space-seperated list of names in a loop within a script. There have to be two versions of that script, one for Windows and one for Linux shell. To loop through one such list is quite easy, in Windows:

@echo off
set WEBSITES=Test1 Test2 Test3

echo WebsiteName=%%A


and in Linux shell:

WEBSITES="Test1 Test2 Test3"

echo "Website= $WEBSITE_ALIAS ..."

But now came a second list of string into play containing the domain names of the website aliases:

WEBSITES="Test1 Test2 Test3"
DOMAINNAMES="www.test1.lan www.test2.lan www.test3.lan"

In a conventional programming language I would just use a for-loop with an index variable and utilize that variable to access both arrays within one loop. But in that batch/shell scripting this turned out to be quite tricky. My solution here was a small inline PERL script. For Windows:

set WEBSITES=Test1 Test2 Test3
set DOMAINNAMES=www.test1.lan www.test2.lan www.test3.lan
perl -e "use strict; die("argv mismatch!") if !@ARGV or scalar(@ARGV) < 2; my @Websites = split(/[s,;|]/, $ARGV[0]); my @Domains = split(/[s,;|]/, $ARGV[1]); die("number of aliases differs from domain names!") if scalar(@Websites) != scalar(@Domains); for(my $i=0; $i<scalar(@Websites); $i++) { system('perl -user /Root/'.$ENV{'WEBSITE_UNIT'}.'/admin -passwd admin -servername '.$ENV{'SERVER_NAME'}.' -alias '.$Website[$i].''); system('perl -user /Root/'.$ENV{'WEBSITE_UNIT'}.'/admin -passwd admin -alias '.$Website[$i].' DomainName="'.$Domains[$i].'"'); }" "%WEBSITES%" "%DOMAINNAMES%"

And for Linux:

WEBSITES="Test1 Test2 Test3"
DOMAINNAMES="www.test1.lan www.test2.lan www.test3.lan"
perl -e 'use strict; die("argv mismatch!") if !@ARGV or scalar(@ARGV) < 2; my @Websites = split(/[s,;|]/, $ARGV[0]); my @Domains = split(/[s,;|]/, $ARGV[1]); die("number of aliases differs from domain names!") if scalar(@Websites) != scalar(@Domains); for(my $i=0; $i<scalar(@Websites); $i++) { system("perl -user /Root/'${WEBSITE_UNIT}'/admin -passwd admin -servername ".$ENV{"SERVER_NAME"}." -alias ".$Website[$i].""); system("perl -user /Root/'${WEBSITE_UNIT}'/admin -passwd admin -alias ".$Website[$i]." DomainName="".$Domains[$i]."""); }' "$WEBSITES" "$DOMAINNAMES"

Notice the different handling of the ticks and quotes and the different access to external parameters. In Windows there’s no difference in PERL’s $ENV hash whether accessing real environment variables or local variables set by the batch script. Not so under Linux: I can only access my system-wide exported environment variable SERVER_NAME using $ENV but not my local script’s WEBSITE_UNIT variable. When using the exec -e with perl for Windows I had to use quotes to wrap the execution PERL-code but for linux shell, I needed single -ticks which can NOT be used inside the PERL code – not event when escapting them like ‘. The single ticks are “reserved” by the shell script for being able to insert shell variables anywhere.