Scripts++

Okay, I'm no programmer, but I enjoy a little dabble, especially when said dabbling makes my life and job easier. I know a little bit of a few languages, my favourite being Perl, which I'm still learning.

Below are a few scripts I've written to automate laborious tasks, something I greatly enjoy :) As you can no doubt see they are rather primitive, and no doubt full of holes. If you see any obvious mistakes I would appreciate if you would let me know, thanks!

Okay, to start with we have a little bash script to first tar then encrypt all the files in the current directory, then tar and encrypt each sub directory individually. Handy for when you have a bunch of large sub directories, which you would prefer backing up seperately than risking one big backup. One gaping hole is that the first backup can't handle files with spaces in the names. Rather than fix this I took the lazy route, and wrote another script for that.

#!/bin/bash

# Backeach - A script to backup and encrypt files in the current
# directory and then each sub-directory in the current directory 
# to a given directory

# Set a safe PATH
PATH="/usr/local/bin:/usr/bin:/bin"
export PATH

# Some functions
error() {
  usage
  exit $1
}

usage() {
  echo "Usage: $PROG destination"
}

# Some variables
PROG="$(basename $0)"
VER="1.0"
guser="60331450"
name="files" # Name to package hanging files as
log="$HOME/.backeach.log"
date="$(date +%x\ %X)"
dest=$1

# Check we got enough arguments
test -z $1 && error 1

# Backup files in current dir
find . -maxdepth 1 -type f -print0 | \
  xargs -0 tar -c | \
    gpg -e -r $guser -o $dest$name.tar.gpg
    
if [ $? -ne 0 ]; then
  echo Failed to backup files in $PWD: $?
else
  echo Files complete.
  echo "$date $PWD" >> $log
fi


# Backup each sub dir individually
for dir in $( find . -maxdepth 1 -type d -printf '%P ' ); do
  tar -c $dir | gpg -e -r $guser -o $dest$dir.tar.gpg
  if [ $? -ne 0 ]; then
    echo Failed to backup $dir: $?
  else
    echo $dir complete.
    echo "$date $PWD/$dir" >> $log
  fi
done
      

Next is the bash script mentioned above to remove spaces from file names and replace them with underscores (_).

#!/bin/bash

# Nospace - A script to remove spaces from file names
# Version: 0.1

for file in *; do
  newfile=$( echo $file | tr ' ' _)
  [ ! -f $newfile ] && [ ! -d $newfile ] && mv "$file" $newfile
done
      

Next is ddu - a little bash script to show the size of each subdirectory in the current directory, in human readable format, sorted by size.

#!/bin/bash

# DDU - Directory disk usage
# Runs 'du -h -s' on each subdir in the current directory
# or a given directory
# Version: 0.4

# Set a safe PATH
PATH="/usr/local/bin:/usr/bin:/bin"
export PATH

SRC=${1:-.}

find $SRC -maxdepth 1 -type d -print0 | xargs -0 du -s | sort -nr | awk '
  BEGIN {
    split("KB,MB,GB", unit, ",")
  }
  {
    i = 1;
    while ($1 >= 1024) {
      $1 /= 1024;
      i++
    }
    $1 = sprintf("%7.2f%s", $1, unit[i]);
    print $0
  }'
      

Next up we have an amusing little perl program which will rot13 encrypt any strings you pass to it - just a bit of fun!

#!/usr/bin/perl -w
foreach (@ARGV) {
  tr/A-Za-z/N-ZA-Mn-za-m/;
  print "$_\n";
}
      

And finally we have digisink, a program to move photos from a digitial camera to a specified project folder. You will need a config file in ~/.digisink for this to work. See my sample config below.

#!/usr/bin/perl
#
# digisink - A program to organise photos from a digital camera
# into manageable projects. Or something.
# Version: 0.3
#

use warnings;
use strict;
use File::Copy;

# Check we got enough arguments
die "usage: $0 projectname\n" unless @ARGV == 1;
my $project = $ARGV[0];

# Some variables
my $config = "$ENV{HOME}/.digisink";
my $srcdir = "$ENV{PWD}";
my ($day, $month, $year) = (localtime(time))[3..5];
my $fdate = sprintf("%4d-%02d-%02d", $year+1900, $month+1, $day);
my (%types, %prefs);

# Process the config file into a hash 
open(CONFIG, "$config") or die "cannot read $config: $!";
while(<CONFIG>) {
  chomp;
  s/#.*//;
  s/^\s+//;
  s/\s+$//;
  next unless length;
  my ($var, $val) = split(/\s*=\s*/, $_, 2);
  # Break TYPEs into their own hash
  if ($var eq "TYPE") {
    my ($type, $exts) = split(/\s*:\s*/, $val, 2);
    $types{$type} = $exts;
  } else {
    $prefs{$var} = $val;
  }
}
close CONFIG or die "cannot close $config: $!";

# Check we got a good method
die "unknown METHOD: $prefs{METHOD}\n" unless $prefs{METHOD} =~ /^(mv|cp)$/;

# Create base directory structure
chdir $prefs{DIR} or die "cannot cd to $prefs{DIR}: $!";
mkdir("$fdate $project", 0755) or die "cannot mkdir $fdate $project: $!";
chdir "$fdate $project" or die "cannot cd to $fdate $project: $!";
foreach (keys %types) { mkdir($_, 0755) or die "cannot mkdir $_: $!" }

# Move or copy files from source dir to new directories
chdir $srcdir or die "cannot cd to $srcdir: $!";
foreach (keys %types) {
  my $type = $_;
  foreach (glob($types{$_})) {
    if ($prefs{METHOD} eq "mv") {
      move($_, "$prefs{DIR}/$fdate $project/$type/$_") or
        die "cannot move file $_: $!";
      print "$_ moved to $prefs{DIR}/$fdate $project/$type/\n";
    } elsif ($prefs{METHOD} eq "cp") {
      copy($_, "$prefs{DIR}/$fdate $project/$type/$_") or
        die "cannot copy file $_: $!";
      print "$_ copied to $prefs{DIR}/$fdate $project/$type/\n";
    }
  }
}
      

... and the config file would be ...

# .digisink - Config file for digisink photo organiser

# Set DIR to destination directory.
DIR=/home/mattq/Documents/photos

# Set METHOD to either 'mv' (move) or 'cp' (copy) files.
METHOD=mv

# Set TYPES for filetypes to process - a directory will be
# created beneath the project dir for each TYPE, and the
# files matching the masks will be moved to those dirs.
TYPE=RAW:*.nef
TYPE=TIFF:*.tiff
TYPE=JPEG:*.jpg *.jpeg