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
SETLOCAL
set WEBSITES=Test1 Test2 Test3

FOR /D %%A IN (%WEBSITES%) DO (
echo WebsiteName=%%A
)
GOTO :EOF

ENDLOCAL

and in Linux shell:

WEBSITES="Test1 Test2 Test3"

for WEBSITE_ALIAS in $WEBSITES ; do
echo "Website= $WEBSITE_ALIAS ..."
done

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 WEBSITE_UNIT=Unit4
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 dosomething.pl -user /Root/'.$ENV{'WEBSITE_UNIT'}.'/admin -passwd admin -servername '.$ENV{'SERVER_NAME'}.' -alias '.$Website[$i].''); system('perl doanotherthing.pl -user /Root/'.$ENV{'WEBSITE_UNIT'}.'/admin -passwd admin -alias '.$Website[$i].' DomainName="'.$Domains[$i].'"'); }" "%WEBSITES%" "%DOMAINNAMES%"

And for Linux:

WEBSITE_UNIT=Unit4
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 dosomething.pl -user /Root/'${WEBSITE_UNIT}'/admin -passwd admin -servername ".$ENV{"SERVER_NAME"}." -alias ".$Website[$i].""); system("perl doanotherthing.pl -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.

Advertisement

Most widely used camera focus length

Recently a collegue of mine argued that he more often needs a small camera focus length (wide-angled) than a zoom focus length (tele). To proof – or confute – this I wrote a small java program which extracts the exif data from a given directory of images and counts the focal length parameters used when taking the pictures. I used the beta version of Drew Noakes’ metadata extractor. As I haven’t coded in java for some time, the code might be a little bit dirty.

import com.drew.imaging.ImageMetadataReader;
import com.drew.imaging.ImageProcessingException;
import com.drew.metadata.exif.ExifReader;
import com.drew.metadata.iptc.IptcReader;
import com.sun.image.codec.jpeg.JPEGCodec;
import com.sun.image.codec.jpeg.JPEGDecodeParam;
import com.sun.image.codec.jpeg.JPEGImageDecoder;
import com.drew.metadata.*;
import com.drew.metadata.exif.ExifDirectory;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.FilenameFilter;
import java.util.*;
import java.util.regex.*;

public class FocalApp {
    public FocalApp(String dirname) {
 // list of files
        File folder = new File(dirname);
        List filelist = new ArrayList();
        this.getFiles(folder, filelist);

 // key - value pairs: focallength - count
        SortedMap focalmap = new TreeMap();

 // for all found files
        for (Iterator it=filelist.iterator(); it.hasNext(); ) {
            String filename = it.next().toString();
 //System.out.println(" --- working on file: "+filename);

            File imageFile = new File(filename);
            try {
                Metadata metadata = ImageMetadataReader.readMetadata(imageFile);
                Directory exifDirectory = metadata.getDirectory(ExifDirectory.class);

                Double cameraFocus = 0.0;
                try {
                    String cameraFocusString = exifDirectory.getString(ExifDirectory.TAG_FOCAL_LENGTH);
                    if (cameraFocusString != null) {
 // value is present
                        cameraFocus = Double.parseDouble(cameraFocusString);
                    }
 //System.out.println("Focus lenght = " + cameraFocus);
                } catch(NumberFormatException nFE) {
 //System.out.println("Not an Integer");
                }

                Integer currentimagecount = 0;
                if(focalmap.containsKey(cameraFocus)) {
 //System.out.println("key already tgere");
                    currentimagecount = focalmap.get(cameraFocus);
                }
                currentimagecount++;
 //System.out.println("putting in map: "+currentvalue);
                focalmap.put(cameraFocus, currentimagecount);
            } catch (ImageProcessingException e) {
                System.err.println("skipping file "+filename+"due to error:"+e);
            }
        }

        Integer totalimagecount = 0;

        TreeSet set = new TreeSet(new Comparator() {
            public int compare(Object obj, Object obj1) {
                int vcomp = ((Comparable) ((Map.Entry) obj1).getValue()).compareTo(((Map.Entry)
                    obj).getValue());
                if (vcomp != 0) return vcomp;
                else return ((Comparable) ((Map.Entry) obj1).getKey()).compareTo(((Map.Entry)
                    obj).getKey());

            }
    });

        set.addAll(focalmap.entrySet());
        System.out.println("focal length in mm;number of images;");
        for (Iterator i = set.iterator(); i.hasNext();) {
            Map.Entry entry = (Map.Entry) i.next();
            Double key = (Double) entry.getKey();
            String keyname;
            if(key == 0) {
                keyname = "?";
            } else {
                keyname = key.toString();
            }
            Integer numimages = (Integer) entry.getValue();
            System.out.println(keyname + ";" + numimages+";");
            totalimagecount+=numimages;
        }

 //System.out.println("total: "+totalimagecount+" images.");
    }

    private void getFiles(File folder, List list) {
        folder.setReadOnly();

        File[] files = folder.listFiles(new ImageFileFilter());
        for(int j = 0; j < files.length; j++) {
            list.add(files[j]);
        }

        File[] subfolders = folder.listFiles();
        for(int j = 0; j  0 && args[0].length() > 0) {
            String imagepath = args[0];
            if(new File(imagepath).exists()) {
                new FocalApp(imagepath);
            } else {
                System.err.println("given image directory not found!");
                System.exit(2);
            }
        } else {
            System.err.println("please give a directory containing the images!");
            System.exit(1);
        }
    }
}

class ImageFileFilter implements FilenameFilter
{
    public boolean accept( File f, String s )
    {
        Pattern pattern = Pattern.compile("([^\s]+(?=\.(jpg|crw|cr2|nef|arw|tiff|tif))\.\2)", Pattern.CASE_INSENSITIVE);
        return pattern.matcher(s).matches();
    }
}

I used JDK 1.6. To build, you’ll need the “metadata-extractor-2.4.0-beta-1.jar” from the metadata extractor page. Then use:

javac -Xlint:unchecked -classpath .;metadata-extractor-2.4.0-beta-1.jar FocalApp.java
jar cfm FocalApp.jar Manifest.txt *.class

to build it. The Manifest.txt is just for entry method:

Manifest-Version: 1.0
Created-By: 1.6.0 (Sun Microsystems Inc.)
Class-Path: metadata-extractor-2.4.0-beta-1.jar
Main-Class: FocalApp

The run the jar, i.e. using:

java -jar FocalApp.jar "/path/to/your/images"

A tricky construct I had to google for after all the java-free months was how to sort a TreeMap by its values and not by the keys. In PERL this is simple (see Perlfaq4 for example):

my @keys = sort { $hash{$a} <=> $hash{$b} } keys %hash;

You get an array containig the hash’s keys, but sorted in the order of the hash’s values.
Now you can just iterate over the keys, displaying the values which could be something like that:

foreach my $Key (@keys) {
print "Key $Key has value $hash{$Key}";
}

In java I couldn’t find such an easy solution. First, I use a TreeMap to store the key-value pairs. This can already sort by keys using a SortedMap. But to turn the sorting around I needed put the entire content of that SortedMap into a TreeSet collection (code line 78), which uses a custom Comperator instance. I got this snippet from this forum page.

You can download the java code in a zip from here.