password generator

applescript can be immensely frustrating at times — trying to get the syntax and structure to work just right is often a torturous process for us novices. but sometimes an applescript solution turns out to be astonishingly easy …

this script is the result of simple paranoia : there are a lot of online services offering to generate randomised passwords for you. now, it seems rational to expect at least some of these services may be malignant — they generate a password for you, then follow your digital trail to where that password is used. again, this may simply be paranoia and have absolutely no bearing at all on reality — or maybe not.

this is a simple script to generate passwords, on your own machine, of whatever length you like *

* yes, whatever length you like, but you may find the script bogs down a bit when generating passwords of more than 50,000 characters — apologies.

a good password is a random collection of upper case letters, lower case letters and digits (even better passwords also include punctuation, but many, many sites do not allow punctuated passwords) — so the first thing we do is create a bunch of lists (see below for an explanation of why we don’t use just one list) :

set mgList1 to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}
set mgList2 to {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"}
set mgList3 to {"1", "2", "3", "4", "5", "6", "7", "8", "9", "0"}
set mgList4 to {mgList1, mgList2, mgList3}

that last list is a list of lists — things will become clearer in a moment.

next we ask the user to specify the length of the password. this uses the handy display dialog command which is standard issue with applescript :

set mgAnswer to display dialog "How long should this password be?" with title "password generator" default answer "8"
set x to text returned of mgAnswer

… and will generate a dialog something like this :
screen grab of password generator dialog
usually the result of a dialog is only button returned — but because we used the default answer property, we can also retrieve the text returned. in this case, the text returned is captured by the variable ‘x’.

the next part is the creation of the password :

set mgPassword to ""
repeat x times
  set mgThisList to some item of mgList4
  set mgPassword to mgPassword & some item of mgThisList
end repeat

first we create an empty variable (mgPassword) then run that variable through a repeat loop ‘x’ number of times — each time adding another randomly chosen letter or digit. this is achieved through the “some item of…” trick. first we randomly choose one of the three lists, then we randomly choose one of the items from that list.

finally, we present the password back to the user :

display dialog "Here you go …" with title "password generator" default answer mgPassword buttons "bonza" default button 1

screen grab of dialog showing generated password

wrap all that in an on run statement and you’ll have a neat little script that looks like this :
screen grab of password generator complete script
save that out as an application somewhere then just double-click to run.

or, if you’re really lazy, you can download the completed app here : password generator

so, why not use just one list?
you could combine all those lists at the start into one like this :

set mgList1 to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", 
"L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", 
"Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", 
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "1", 
"2", "3", "4", "5", "6", "7", "8", "9", "0"}

… but your passwords will be less likely to contain digits. with three lists you have a 1 in 3 chance that a digit will be chosen on each pass through the repeat loop. with only one list that chance is reduced to 1 in 6.2.

yeah, sure, but why not just do it like this? …

set mgList1 to {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", 
"L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", 
"Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", 
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "1", 
"2", "3", "4", "5", "6", "7", "8", "9", "0", "1", "2", "3", "4", "5", 
"6", "7", "8", "9", "0"}

because i didn’t bloody think of that when i first wrote the script — now shut up!
… but also because, with the three list version, you have the opportunity to extend the script to ensure that, for example, you don’t get three capitals in a row (so there).

now for your homework : how would you alter the script so that it generates five passwords at a time :
screen grab of dialog with five generated passwords

hoping you all have yourselves a grunting great christmas.

if you find this script handy, feel free to spread a little christmas cheer by hitting the donate button — thanks

macgrunt icon

Advertisements

renaming finder items III

renaming finder items introduced a couple of ways to batch-rename files in the finder — using automator or applescript — by replacing characters or removing parts of the file name. renaming finder items II showed how to map a bunch of files to a completely different set of file names.

Applescript Icon this post will show another way to rename files — based on their creation date — using applescript. this workflow came about due to the diabolical way that digital cameras name their files. here’s a screen grab of a typical bunch of images :
screen grab of folder of images with diabolical file names

for this workflow, we’d like the order of the filenames to match the date the photo was taken — to make sorting, selecting, archiving, etc. just a little easier. the current naming is made more problematic because files are coming from more than one camera. well, we don’t have to put up with that crap — let’s just rename them.

set mgFolder to choose folder
tell application "Finder"
  set mgFiles to items of mgFolder
end tell
repeat with mgFile in mgFiles
  set mgPath to POSIX path of (mgFile as string)
  set mgCreation to creation date of mgFile
  set mgDate to short date string of mgCreation
  set mgTime to time string of mgCreation
  
  -- rest of script here
end repeat

first we get the user to select a folder of images to process, then we get references to the files in that folder, then we start a repeat loop to process each file in turn. we capture the following information :
mgCreation – for example “Thursday, 4 October 2012 17:13:18 ” – from which we extract :
mgDate – “04/10/12” (that’s dd/mm/yy) and
mgTime – “17:13:18 ” (that’s hh/mm/ss)

if you find that mgDate or mgTime give you the data in a different format, you will need to update the next part of the script accordingly or, if you prefer, change your language and text system preferences :
screen grab showing language and text system preferences window

we’re going to use mgDate and mgTime to create a file name like this “yymmdd_hhmmss” — so, we need to strip out the slashes and colons, reverse the order of the date, and introduce an underscore :

  set text item delimiters of AppleScript to ":"
  set mgTime to text items of mgTime --gives {"17", "13", "18 "}
  set text item delimiters of AppleScript to "/"
  set mgDate to reverse of text items of mgDate --gives {"12", "10", "04"}
  set text item delimiters of AppleScript to ""
  set mgDate to mgDate as string --gives "121004"
  set mgTime to mgTime as string --gives "171318 "
  set mgTime to text items 1 thru 6 of mgTime as string
  
  set mgNewName to mgDate & "_" & mgTime

this uses the handy “reverse of” command — taking a list of items and rearranging them in reverse order. also notice the second last line — we need that because mgTime has a trailing space we need to get rid of. this method works no matter how many trailing spaces there are because we’re saying “just give me the first six characters and ditch the rest”.

now, that’s the bulk of the script done — we also need to capture the extension of the file (mgExtension), create a filepath for the renamed file (mgFinalPath), and then do the renaming with a shell script. the shell script basically says — move this file, without any prompts, from this filepath to that filepath (you could also use this method to move the file to a completely different location — but we’re just renaming the file and leaving it in the same location) :
danger : do not run this next script — it is for demonstration purposes only – you’ve been warned!

set mgFolder to choose folder
tell application "Finder"
  set mgFiles to items of mgFolder
end tell

repeat with mgFile in mgFiles
  set mgPath to POSIX path of (mgFile as string)
  set mgCreation to creation date of mgFile
  set mgDate to short date string of mgCreation
  set mgTime to time string of mgCreation
  
  set text item delimiters of AppleScript to ":"
  set mgTime to text items of mgTime
  set text item delimiters of AppleScript to "/"
  set mgDate to reverse of text items of mgDate
  set text item delimiters of AppleScript to "."
  set mgExtension to text item -1 of (mgFile as string)
  set text item delimiters of AppleScript to ""
  set mgDate to mgDate as string
  set mgTime to mgTime as string
  set mgTime to text items 1 thru 6 of mgTime as string
  
  set mgNewName to mgDate & "_" & mgTime

  set mgFinalPath to mgFolder & mgNewName & "." & mgExtension as string

  do shell script "mv -f " & quoted form of POSIX path of mgPath & space & quoted form of POSIX path of mgFinalPath
end repeat

if we run this script on that original folder of images we’ll get this :
screen grab of file names after processing with first script

well, that looks pretty good at first glance — all the files have been renamed successfully … except … the very observant will notice that this folder has only 81 items, whereas the original folder had 103. wtf?

the problem is that it’s quite possible to have more than one file with a creation date of “04/10/12 17:13:18” — digital cameras can flick off quite a few images in a second. the script in its current form will simply write over a file with a duplicate filename. so we need to build in some method to check if a file with a particular name already exists and, if so, create a different name. here’s one way :

  set mgFinalPath to mgFolder & mgNewName & "_1" & "." & mgExtension as string
  tell application "Finder"
    set x to 2
    repeat
      set mgExists to exists file mgFinalPath
      if mgExists is true then
        set mgFinalPath to mgFolder & mgNewName & "_" & x & "." & mgExtension as string
        set x to x + 1
      else
        exit repeat
      end if
    end repeat
  end tell

this time we’re appending “_1” to each filename. then the script enters a repeat loop to check if a file with that name already exists. if so, it will change the end to “_2” and then check that, incrementing by one on each pass through the loop. once it reaches a name that has not already been used it exits the repeat loop. this way we don’t lose any files :
screen grab of filenames using final script

this is the final form of the script. it’s always a good idea to test new scripts on duplicate files before implementing them into your workflow — just to make sure they do what you expect (and don’t do what you don’t expect) — you’ve been warned again :

set mgFolder to choose folder
tell application "Finder"
  set mgFiles to items of mgFolder
end tell

repeat with mgFile in mgFiles
  set mgPath to POSIX path of (mgFile as string)
  set mgCreation to creation date of mgFile
  set mgDate to short date string of mgCreation
  set mgTime to time string of mgCreation
  
  set text item delimiters of AppleScript to ":"
  set mgTime to text items of mgTime
  set text item delimiters of AppleScript to "/"
  set mgDate to reverse of text items of mgDate
  set text item delimiters of AppleScript to "."
  set mgExtension to text item -1 of (mgFile as string)
  set text item delimiters of AppleScript to ""
  set mgDate to mgDate as string
  set mgTime to mgTime as string
  set mgTime to text items 1 thru 6 of mgTime as string
  set mgNewName to mgDate & "_" & mgTime
  
  set mgFinalPath to mgFolder & mgNewName & "_1" & "." & mgExtension as string
  tell application "Finder"
    set x to 2
    repeat
      set mgExists to exists file mgFinalPath
      if mgExists is true then
        set mgFinalPath to mgFolder & mgNewName & "_" & x & "." & mgExtension as string
        set x to x + 1
      else
        exit repeat
      end if
    end repeat
  end tell
  
  do shell script "mv -f " & quoted form of POSIX path of mgPath & space & quoted form of POSIX path of mgFinalPath
end repeat

now, of course, you can run this script for any kind of file, not just images. but in its current form it has no error handling for if it encounters a folder. we’ve fixed it so it won’t have a hassle with duplicate files, but if it hits a folder you’ll get something like this :
screen grab of error message if script encounters a folder

dammit you say. but the fix is actually very simple — just change the line “set mgFiles to items of mgFolder” to “set mgFiles to files of mgFolder” and the folders will be skipped — no drama.

but that’s not all …
just to be safe, or because your workflow calls for it — you could change the script so that it makes a copy of the file — giving the new name to the copy and leaving the original untouched.

and again, the solution is very simple — change the shell script from move (mv) to copy (cp) :

do shell script "cp -f " & quoted form of POSIX path of mgPath & space & quoted form of POSIX path of mgFinalPath

you can get a copy of the RenameByDate app here

keep grunting


• related post : renaming finder items : renaming using automator or applescript.
• related post : renaming finder items II : renaming by list.
• related post : renaming finder items IV : easy renaming.

macgrunt icon

turn an applescript into a service

the applescript outlined in email file from finder II takes selected items in the finder and attaches them to a new outgoing email message. it allows for multiple recipients from your address book and automatically adds a subject line and some basic content to the email.

there are a few ways to make that applescript accessible in the finder but the most convenient is to turn it into a service. this method is for OS X 10.6 and later — you can see a similar procedure for earlier operating systems in the post get file path of finder items.

Automator Iconapplescript services can be created through automator — the automation program that comes standard on every mac since OS X 10.4 (you’ll find it in your applications folder).

when you first open automator you’ll see this screen — choose service :
screen grab of automator startup screen

this next screen grab shows a couple of different things you need to do. in the main window on the right are two dropdowns — set the first to files or folders and the second to finder. in the search field on the left type ‘applescript’ to easily find the run applescript action. drag and drop this action into the right window :
screen grab of automator with service being built

then paste your applescript into that action window — you can entirely replace the default script. this is what it should look like after you hit the compile button (hammer) :
screen grab of automator service window with applescript in place

save that out and you’re done. now, whenever you want to email something from the finder just select it and right-click — you’ll see your service near the bottom of the contextual menu :
screen grab showing the service as a contextual menu item

you can also activate it under finder > services :
screen grab of services menu

under that menu you can also access the services preferences. this allows you to even set a keyboard shortcut for your new handy service :
screen grab of services preferences window

of course, services aren’t just restricted to running your applescripts. investigate the full power of automator … and get grunting.

macgrunt icon

iCal and address book

what in the name of hideous monstrosities was apple thinking when they shipped lion with those fugly interfaces for iCal and address book? that faux baby-poo leather and torn page look is the naffest thing apple have done since that white crumb-catcher keyboard.

the styling for the app must have been outsourced to some executive’s pampered, brain-damaged, drug-addled nephew.

thankfully it’s easy to fix — to ease your troubled eyeballs, just replace all the graphics in the app package with new ones.

you can find aluminium-look graphics and instructions on how to install them at MacNix. if you appreciate how easy it is — flick MacNix a donation.

screen grab of iCal before and after updating graphics

macgrunt icon

deleting empty folders

some workflows involve creating standard sets of job folders at the beginning of a project and then going through and cleaning out any unused folders at the end. this is a good way to standardise archiving procedures but it can also be tedious to clean up once the project is done. this post shows how to easily delete empty folders using applescript.

thanks to xander at cadcoder (sorry this link seems to be broken at the moment — Aug 2013) for the basic structure of the shell command.

Applescript Icon to follow along, copy and paste the script examples into applescript editor (found in applications > utilities) or, for older OS versions, script editor (applications > applescript). we’re going to set this one up as a droplet (so you’ll save it from script editor as an application). you can save a droplet to your desktop or drag it to your sidebar for easy access. to run the script just drag and drop a folder onto the droplet’s icon.

the basic form of the script goes like this :

on open mgItem
  do shell script "cd " & quoted form of POSIX path of mgItem & " && find . -type d -empty -delete;"
end open

in the shell, a folder is known as a directory (d). the first part of the command changes the current directory (cd) to our dropped folder. then it performs a search of that folder (find) for empty subfolders (-type d -empty) and deletes them. simple.

this works a treat — most of the time. but macs have these dastardly hidden files, the most common of which are the good old .DS_Store files which help the finder do its thing. a folder containing nothing but a .DS_Store file looks empty, but as far as the shell is concerned, it’s not.

so, here’s a variation to deal with that issue — first delete any .DS_Store files, then delete empty folders :

on open mgItem
  do shell script "cd " & quoted form of POSIX path of mgItem & " && find . -name .DS_Store -delete; find . -type d -empty -delete;"
end open

ok, that’s better. but to make the droplet a little more user-friendly, we should ensure it can handle multiple dropped folders — rather than having to drop them one at a time :

on open mgItems
  repeat with mgItem in mgItems
    do shell script "cd " & quoted form of POSIX path of mgItem & " && find . -name .DS_Store -delete; find . -type d -empty -delete;"
  end repeat
end open

and to make it more robust, it should not have a conniption if we inadvertently drop files instead of, or as well as, folders :

on open mgItems
  repeat with mgItem in mgItems
    set mgItem to mgItem as alias
    tell application "System Events" to set theClass to get class of item (mgItem as string)
    
    if (theClass as string) contains "cfol" then
      do shell script "cd " & quoted form of POSIX path of mgItem & " && find . -name .DS_Store -delete; find . -type d -empty -delete;"
    end if
    
  end repeat
end open

that’s about it — super fast, super easy way to clean out any unused folders.

for those interested in this sort of thing…
applescript droplet icon when an applescript is saved as a droplet, its icon includes a downwards pointing arrow. the on open mgItems line at the start of this script is what allows the saved application to run as a droplet. without that on open command you’d need to find some other way to reference the folders to be processed. renaming finder items shows a couple of other ways you could do this.

macgrunt icon