I have an unusual workflow when copying photos from my camera's SD card to my Windows desktop. For any photos I my desktop, I want to also delete on the SD card. In this post (part 1 of 2), I describe my PowerShell script to "repeat" what I deleted on the desktop on the SD card. In my next post, part 2, I expand the PowerShell script to (try) monitor for file system changes instead.

My workflow:

  1. I copy all files (JPGs and RAWs) from the camera's SD card to my desktop in a brand new folder. The filenames have a fixed prefix followed by 4 numbers, e.g. DSCF0001.JPG and the associated RAW DSCF0001.RAF.
  2. On my desktop, I use FastStone Image Viewer to view and compare photos, and delete the JPGs I don't want.
  3. Now, I delete the same files (both JPEG and RAW) on the SD Card, but leave the retained files alone - just in case I decide to re-process the RAWs in-camera, or so that I can copy good photos from the SD card to another notebook / desktop.

PowerShell Script to Check for Deleted Files

Say I have DSCF0001.JPG, DSCF0002.JPG, DSCF0003.JPG, DSCF0004.JPG, and I delete DSCF0002.JPG.

I wrote a PowerShell script, find_deleted.ps1, to:

  1. Get the highest and lowest JPG file number in the folder e.g. 1 to 4 - the script uses a simple Regular Expression to extract the numeric part (-replace is a String function)
  2. Run through the sequence from first to last, and check if a file still exists with that file name - e.g. the file with number 2 has been deleted.
  3. Generate a batch file with delete commands of the photos I deleted - e.g. outputs a script containing del DSCF0002.JPG.

You shouldn't have a file called run_delete.cmd already, unless you really want to append to it.

param (
 [int]$start = -1,
 [int]$end = -1,
 $out = "run_delete.cmd"
)
$folder = pwd
$output = "$folder\$out"
$files = Get-ChildItem -Path ".\DSCF*.JPG" -Name | Sort-Object
if ($files -eq $null) { Write-Host "No JPGs found!"; exit }
$first = if ($start -ge 0) { $start } else { [int] ($files[0] -replace 'DSCF(\d+).JPG','$1') }
$last = if ($end -gt $start) { $end } else { [int] ($files[$files.length-1] -replace 'DSCF(\d+).JPG','$1') }
Write-Host "Checking JPG files from $first to $last"
for ($i=$first; $i -le $last; $i++) {
 $file = 'DSCF' + $i.ToString('0000') + '.*'
 if (!(Test-Path $file)) {
  $file1 = 'DSCF' + $i.ToString('0000') + '.JPG'
  $file2 = 'DSCF' + $i.ToString('0000') + '.RAF'
  Write-Host "del ""$file1"" ""$file2"""
  Out-File $output -Encoding ASCII -Append -InputObject "del ""$file1"" ""$file2"""
 }
}

Running the PowerShell Script

Now, to run the script from the Command Prompt (cmd) in the folder where my photos reside after deleting any JPGs that I don't want:

cd my_photos
powershell -ExecutionPolicy ByPass -File find_deleted.ps1 

If you run an unsigned script powershell find_deleted.ps1, you may get a RuntimeExeception that the script "cannot be loaded because the execution of scripts is disabled on this system." To avoid this, the -ExecutionPolicy is required.

If I deleted the first or last file, then of course the number range determined by the script would be incorrect. So, the -start parameter sets the start, and -end optionally sets the end number.

powershell -ExecutionPolicy ByPass -File find_deletes.ps1 -start 10 -end 20

The script creates the file run_delete.cmd, which I move to the SD card (the same folder where the photos reside) and run it manually. Something like:

move run_delete.cmd F:\DCIM\
F:\
cd DCIM
run_delete.cmd

Conclusion

A reasonably simple script, since PowerShell has good support for Regular Expressions. On to part 2...