Matt Brock's SysAdmin+ Blog

The Writings of a Freelance Infrastructure Consultant

Bash script to extract photos from vCard files —

If you ever find yourself wanting to extract the contact photos from vCard/VCF files then this may be the script for you. I know that this works on a vCard export of multiple contacts from the Contacts app in OS X Mountain Lion. It’s possible you might have to tweak it a bit for vCard files from other sources. This script also resizes the exported photos to 201×201 pixels. If you want to modify this conversion then just change the “convert” line near the bottom accordingly. If you don’t want to resize the photos then simply delete this line and the bit where it checks for the presence of ImageMagick (or comment these bits out). If you do want to resize the images then you’ll need to have

ImageMagick installed (“brew install imagemagick” if you’re on a Mac with Homebrew installed, which it really ought to be). You’ll also need to make sure you have base64 installed, which it probably already will be. If you’re copying and pasting this script, make sure you replace the ^M near the bottom of the script with a literal ^M (normally you type Ctrl-V then Ctrl-M to get a literal ^M). The script will take the vCard file you specify as input and create a subdirectory called photos containing the photos from your contacts.

#!/bin/bash

progname=$(basename $0)
usage="Usage:\t$progname -f vCard_file \n\t$progname -h"

while getopts "f:h" options ; do
  case $options in
    f) vcard_file=$OPTARG ;;
    h) echo -e $usage ; exit ;;
  esac
done

if [ ! "$vcard_file" ] ; then
  echo -e $usage
  exit 1
fi

if [[ ! $(head -1 $vcard_file) =~ "BEGIN:VCARD" ]] ; then
  echo "$vcard_file does not appear to be a vCard file"
  exit 1
fi

if ! which base64 > /dev/null ; then
  echo "base64 is not installed"
  exit 1
fi

if ! which convert > /dev/null ; then
  echo "ImageMagick is not installed"
  echo "Either install it or comment out the ImageMagick stuff"
  exit 1
fi

if [ -d photos ] ; then
  echo "photos directory already exists"
  exit 1
fi

if ! mkdir photos ; then
  echo "Failed to create photos directory"
  exit 1
fi

regex1="^N:"
regex2="^PHOTO;"
regex3=":|;"

echo -ne "Extracting photos"
cat $vcard_file | while read line ; do
  if [[ $line =~ $regex1 ]] ; then
    filename=$(echo $line | awk -F '[:;]' '{printf("%s%s",$3,$2)}' | \
      sed 's/[^a-zA-Z]//g')
    echo -ne "."
    cat /dev/null > photos/$filename.tmp
  elif [[ $line =~ $regex2 ]] ; then
    echo $line | sed 's/PHOTO;ENCODING=b;TYPE=JPEG://' >> photos/$filename.tmp
  elif [[ ! $line =~ $regex3 ]] ; then
    echo $line >> photos/$filename.tmp
  fi
done
echo -ne " done\n"

echo -ne "Removing empty photos..."
find photos -empty -exec rm -f {} +
echo -ne " done\n"

echo -ne "Converting photos"
cd photos
for file in *.tmp ; do
  filename=$(echo $file | awk -F '.' '{print $1}')
  echo -ne "."
  cat $file | tr -d '\n' | tr -d '^M' | base64 -d > $filename.jpg
  convert $filename.jpg -resize 201x201 $filename.jpg
  rm -f $file
done
echo -ne " done\n"

Categorised as: SysAdmin


5 Comments

  1. mista busta says:

    Nice little script. My only suggestion would be maybe scatter a few comments in the code for explanation. Thanx!

    • Matt Brock says:

      Thanks. I take your point about the comments but I thought the way I’d laid it out with echo commands made it fairly clear what was going on. My time is limited and I wanted to get it posted in a working form today before I had to move onto something else, but I may come back and add comments etc. at a later date if I get a spare few minutes.

      Alternatively, feel free to send me a version with comments added and if it looks OK I’ll update the post with credit given to yourself accordingly.

      Otherwise, hope it proves useful! Cheers.

  2. rcjhawk says:

    Using Ubuntu 12.10, with ImageMagick and base64 installed, and a vCard created by my Android phone, I get the errors:

    Extracting photos. done

    Removing empty photos… done

    Converting photos.base64: invalid input

    convert.im6: Not a JPEG file: starts with 0x3c 0×73 `JohnSmith.jpg’ @ error/jpeg.c/JPEGErrorHandler/316.

    convert.im6: no images defined `JohnSmith.jpg’ @ error/convert.c/ConvertImageCommand/3044.

    done

    Any ideas? I’m probably missing something obvious. The vCard has the line: PHOTO;ENCODING=BASE64;JPEG:, and there is definitely a picture that shows up on my phone.

  3. rcjhawk says:

    OK, after a little debugging:

    In Ubuntu, at least, the line in the vCard file that starts with “PHOTO;ENCODING” is different. So you need to replace the line

    echo $line | sed ‘s/PHOTO;ENCODING=b;TYPE=JPEG://’ >> photos/$filename.tmp

    with

    echo $line | sed ‘s/PHOTO;ENCODING=BASE64;JPEG://’ >> photos/$filename.tmp

    There is probably a regexp that can handle both cases, if I figure it out I’ll post it here.

    And in what I suspect is a Unix thing, looking for a ^M at the end of the line leads to an error, since Unix text lines only end with ‘\n’ (^J in your program, I believe). So in that case replace

    cat $file | tr -d ‘\n’ | tr -d ‘^M’ | base64 -d > $filename.jpg

    by

    cat $file | tr -d ‘\n’ | base64 -d > $filename.jpg

    There is a test to see if lines end with ^M^J or just ^M, again if I generalize the line I’ll post it here.

    Once I did that, everything worked great. Thanks for the script, it made a difficult problem easy.

    • Matt Brock says:

      Thanks very much for all the handy feedback. Glad you got it working.

      I’m hopefully getting an Android tablet to play with soon, so I’ll update this with a version which works on vCard files exported from both OS X and Android.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>