Improved Finder/Terminal tools

A couple of days ago, I got an email from Loren Halter, who had some improvements to my Finder/Terminal tools. I was going to add another update to that post, but realized I had more to say about Loren’s stuff than would fit comfortably in an update. So here we are with a new post.

First, Loren put his work in this gist, so you can review and copy them for your own use. There are three functions Loren includes in their .zshrc dotfile1 to ease the use of the Finder and Terminal together. They are lsf, cdf, and sel, and we’ll go through each of them in turn.

lsf is a variant of the ls command that lists, in the Terminal, the contents of the front Finder window. I can’t say that I foresee myself using this function, as the Finder window itself shows its contents. Presumably, Loren uses this to feed a list of file names to another command, but I prefer doing that sort of thing with my Terminal’s working directory set to the directory that contains the files of interest; in such cases, ls suffices. But it may be just the thing for you.

Next comes cdf, which changes your working directory to that of the front Finder window. It is essentially the cd command combined with my ;dir abbreviation. While it’s not a huge timesaver, I do this combination enough that I decided to add it to my .bashrc. As I was scrolling through the file to find a good place to put it, I found a function called cdff, which was apparently a long-forgotten attempt on my part to do the same thing (I assume the ff part meant “front Finder”). I won’t show it to you, because both the AppleScript and shell scripting aspects of it were a horrible mess. I suspect it didn’t even work, which is why I don’t remember anything about it.

Anyway, I decided to take Loren’s idea for cdf and make my own version of it:

bash:
1:  function cdf() {
2:    target=$(osascript -e 'tell application "Finder" to return POSIX path of (target of front Finder window as alias)')
3:    cd "$target"
4:  }

Comparing Loren’s version with mine, you’ll see the error handling code in theirs that I’ve not included in mine—I’m just an outlaw, I guess. What I wanted was for my cdf to work exactly as combining cd with ;dir would, and I don’t really see much danger in not including the try/on error code.

(What Loren’s code handles that mine doesn’t is the case in which there are no open Finder windows. Loren’s cdf will then cd into the Desktop folder; mine will belch out an error message. I don’t want that protection because my goal is to never work in the Desktop directory. In my experience, doing so, even “temporarily,” leads to a clutter of files on the Desktop that sit there far longer than they should. If you’re more disciplined than I am, by all means, use Loren’s version.)

Which leaves us with sel. I mentioned in my earlier post that my version of sel could take several seconds to run if it’s selecting hundreds of files. In my tests, Loren’s code runs in about a third of the time as mine. That considerable reduction in runtime came from changing the way AppleScript constructs the list of files to select. You may recall that my version of sel generates AppleScript that looks like this

applescript:
tell application "Finder"
    set theFolder to target of front window as alias
    set theFiles to {}
    tell folder theFolder
            set end of theFiles to file "20240915-001.jpg"
            set end of theFiles to file "20240915-002.jpg"
            set end of theFiles to file "20240915-003.jpg"
            set end of theFiles to file "20240915-004.jpg"
            set end of theFiles to file "20240915-005.jpg"
            set end of theFiles to file "20240915-006.jpg"
            set end of theFiles to file "20240915-007.jpg"
            set end of theFiles to file "20240915-008.jpg"
            set end of theFiles to file "20240915-009.jpg"
            set end of theFiles to file "20240915-010.jpg"
        end tell
    select theFiles
    return
end tell

and then runs it. Loren’s sel generates AppleScript that looks more like this:

applescript:
tell application "Finder"
    set theFiles to {}
    set end of theFiles to POSIX file "/Users/drdrang/Library/Mobile Documents/com~apple~CloudDocs/blog-stuff/finder-terminal/20240915-001.jpg" as alias
    set end of theFiles to POSIX file "/Users/drdrang/Library/Mobile Documents/com~apple~CloudDocs/blog-stuff/finder-terminal/20240915-002.jpg" as alias
    set end of theFiles to POSIX file "/Users/drdrang/Library/Mobile Documents/com~apple~CloudDocs/blog-stuff/finder-terminal/20240915-003.jpg" as alias
    set end of theFiles to POSIX file "/Users/drdrang/Library/Mobile Documents/com~apple~CloudDocs/blog-stuff/finder-terminal/20240915-004.jpg" as alias
    set end of theFiles to POSIX file "/Users/drdrang/Library/Mobile Documents/com~apple~CloudDocs/blog-stuff/finder-terminal/20240915-005.jpg" as alias
    set end of theFiles to POSIX file "/Users/drdrang/Library/Mobile Documents/com~apple~CloudDocs/blog-stuff/finder-terminal/20240915-006.jpg" as alias
    set end of theFiles to POSIX file "/Users/drdrang/Library/Mobile Documents/com~apple~CloudDocs/blog-stuff/finder-terminal/20240915-007.jpg" as alias
    set end of theFiles to POSIX file "/Users/drdrang/Library/Mobile Documents/com~apple~CloudDocs/blog-stuff/finder-terminal/20240915-008.jpg" as alias
    set end of theFiles to POSIX file "/Users/drdrang/Library/Mobile Documents/com~apple~CloudDocs/blog-stuff/finder-terminal/20240915-009.jpg" as alias
    set end of theFiles to POSIX file "/Users/drdrang/Library/Mobile Documents/com~apple~CloudDocs/blog-stuff/finder-terminal/20240915-010.jpg" as alias
    select theFiles
    return
end tell

Now, I think my code is more elegant looking, and it takes advantage of some nice AppleScript features; but again, Loren’s runs in about a third of the time. Loren believes this speedup comes from his code using the full path to each file, eliminating the work AppleScript has to do in the tell folder block of my code. I agree.

When faced with the choice between elegant but slow autogenerated code that no one sees and cruder but speedy autogenerated code that no one sees, I know which one I’m going to take. I still preferred having sel as a command script rather than a function, and I also wanted it written in my style to make it easier to update if necessary. So I incorporated Loren’s insight into my sel, giving me this:

bash:
 1:  #!/bin/zsh
 2:  
 3:  # Open Finder window to the current directory and select all the files
 4:  # in that directory whose names are passed to this script via stdin.
 5:  
 6:  # Open a Finder window to the current directory. 
 7:  open .
 8:  
 9:  # Construct the AppleScript in three parts.
10:  # 1. Initialize variables and start telling theFolder.
11:  applescript='tell application "Finder"
12:    set theFiles to {}
13:  '
14:  
15:  # 2. Add all the files from stdin to theFiles list
16:  while read f; do
17:    thisFile="$PWD/$f"
18:    applescript+="  set end of theFiles to POSIX file \"$thisFile\" as alias
19:  "
20:  done
21:  
22:  # 3. Stop telling theFolder and select theFiles. Return nothing.
23:  applescript+='  select theFiles
24:    return
25:  end tell'
26:  
27:  # Run the AppleScript.
28:  echo "$applescript" | osascript -

The key is in Lines 17–18, where the full path to each file is assembled using the PWD environment variable.

Again, Loren’s code has error handling that mine doesn’t, although in this case the error handling basically just throws up a dialog box telling you what the failure is. In my sel, error messages are printed in the Terminal by the system.

Thanks to Loren for the code improvements and a better understanding of AppleScript.


  1. I think they’ll all work just as well from a .bashrc file if you’re a stick-in-the-mud like me.