How do I close a window from an application, passing the file name?

9

6

I’m trying to produce an Applescript-based shell command that tells the Preview application from Mac OS X to close a particular window.

#!/bin/sh

osascript <<EOF
tell application "Preview"
   close "$1"
end tell
EOF

But this doesn’t work : I get the error message

25:52: execution error: Preview got an error: "musixdoc.pdf" doesn’t understand the close message. (-1708)

Related question: How do I close an OS X application from the command line using a alias defined in my .bash_profile?

Ewan Delanoy

Posted 2013-01-01T09:55:49.653

Reputation: 191

This should work as expected. How are you calling your script, exactly? Note: Your script will not close a particular window. It will quit the application, thus closing all document windows the application might have open. In order to close a particular document the application has opened but not quit the application per se, you need a different script. While most applications quit when their last document window is closed, not all do—this also depends on the version of OS X you're using. – slhck – 2013-01-01T10:01:53.643

@slhck : "close a particular document the application has opened but not quit the application per se" is exactly what I need. Do you happen to know someplace where such a "different script" is explained? – Ewan Delanoy – 2013-01-01T10:38:12.630

Answers

10

Getting Preview.app to accept AppleScript commands

By default, AppleScripting Preview won't work because Preview is missing the necessary dictionary. To fix this, check Lauri's answer here, which explains setting NSAppleScriptEnabled for Preview.app.

Quit Preview.app, then open a terminal and enter:

sudo defaults write /Applications/Preview.app/Contents/Info NSAppleScriptEnabled -bool true
sudo chmod 644 /Applications/Preview.app/Contents/Info.plist
sudo codesign -f -s - /Applications/Preview.app


Closing a window from an application

1) By window index or name of the window

The command to close a window of any named application would be something like this:

tell application "Preview" to close window 1

… or if you want to close a named document window, e.g. foo.jpg:

tell application "Preview" to close (every window whose name is "foo.jpg")

So, in your shell script that'd be:

#!/bin/sh
osascript <<EOF
tell application "Preview"
  close (every window whose name is "$1")
end tell
EOF

Here, the first argument passed to the script is the name of the window you want to close, e.g. ./quit.sh foo.jpg. Note that if your file contains spaces, you have to quote the filename, e.g. ./quit.sh "foo bar.jpg".

Or if you want to close arbitrary windows from any application, use this:

#!/bin/sh
osascript <<EOF
tell application "$1"
  close (every window whose name is "$2")
end tell
EOF

Here, you'd use ./quit.sh Preview foo.jpg for example.

2) By file name

If you want to close a window that belongs to a certain document, but supplying the file name, you need something else. This is because a multi-page PDF could be displayed as foo.pdf (Page 1 of 42), but you'd just want to pass foo.pdf to the AppleScript.

Here we iterate through the windows and compare the filenames against the argument passed to the script:

osascript <<EOF
tell application "Preview"
    set windowCount to number of windows
    repeat with x from 1 to windowCount
        set docName to (name of document of window x)
        if (docName is equal to "$1") then
            close window x
        end if
    end repeat
end tell
EOF

Now you can simply call ./quit.sh foo.pdf. In a generalized fashion, for all apps with named document windows, that'd be:

osascript <<EOF
tell application "$1"
    set windowCount to number of windows
    repeat with x from 1 to windowCount
        set docName to (name of document of window x)
        if (docName is equal to "$2") then
            close window x
        end if
    end repeat
end tell
EOF


Caveat: Auto-closing Preview.app

Preview.app is one of these applications that automatically quits once its last document window is closed. It does that in order to save memory and "clean up". To disable this behavior, run the following:

defaults write -g NSDisableAutomaticTermination -bool TRUE

Of course, to undo that, change TRUE to FALSE.


Using functions instead of scripts

Finally, I'd suggest putting your scripts into a function that is always available in your shell. To do this, add the scripts to your ~/.bash_profile. Create this file if it doesn't exist.

cw() {
osascript <<EOF
tell application "$1"
    set windowCount to number of windows
    repeat with x from 1 to windowCount
        set docName to (name of document of window x)
        if (docName is equal to "$2") then
            close window x
        end if
    end repeat
end tell
EOF
}

Once you save your bash profile and restart the shell, you can call cw Preview foo.pdf from everywhere.

slhck

Posted 2013-01-01T09:55:49.653

Reputation: 182 472

@slhck Could you expound on the ramifications of sudo codesign -f -s - /Applications/Preview.app, reference my recent answer? I know this is not a true answer, but I wish to highlight a possible glitch before someone gets into a deep mess. – David C – 2014-08-03T22:40:12.873

I think set docName to (name of document of front window) should be set docName to (name of document of window x) - the front window stays the same, so the loop always uses the same window. Also, this doesn't solve the "<filename> (1 page)" issue, does it ? – ssc – 2014-12-04T15:39:04.477

Instead of if (docName is equal to "$2") then, if (docName starts with "$2") then might help with the "<filename> (1 page)" issue... – ssc – 2014-12-04T15:45:17.367

I tried this. This time I get no error message, but the script still doesn't work : the window stays visible on screen. – Ewan Delanoy – 2013-01-01T10:43:32.690

It worked for me. If you open a document in Preview.app, then open AppleScript Editor, and enter tell application "Preview" to windows, does that give you any results? What exact script are you running? – slhck – 2013-01-01T11:38:41.873

I don’t use the AppleScript editor, I only use "embedded applescript code" in a bash script, as explained in the OP,stored in a .sh file. – Ewan Delanoy – 2013-01-01T12:54:22.633

In fact, I’m usually quite happy with shell scripts and this is my first applescript. I just did what you advised in your last comment, and got : "{window id 113 of application "Preview"}" as an answer. Is that what I should get? – Ewan Delanoy – 2013-01-01T13:30:10.490

Also, when I compile << tell application "Preview" to close "musixdoc.pdf" >> with the applescript editor, it does not work either : the musixdoc.pdf window stays visible on the screen. – Ewan Delanoy – 2013-01-01T13:39:31.190

when I type << tell application "Preview" to close window 1 >> it does work however. But I want my final shell command (called close_in_preview) to work with an arbitrary filename, not just the "first" window. – Ewan Delanoy – 2013-01-01T13:44:07.303

I tried your cw function, but unfortunately it doesn’t work : one more time I get no error messages but the window does not disappear from the screen (I tried << cw Preview musixdoc.pdf >> and <<cw /Applications/Preview.app musixdoc.pdf >> and both result in the same behavior). As I informed you in an earlier comment, it does work however when I use the AppleScript editor directly. – Ewan Delanoy – 2013-01-01T14:52:35.287

Very weird. Are you sure the file name you're using is the correct one? So osascript -e 'tell application "Preview" to close window 1' works and osascript -e 'tell application "Preview" to close (every window whose name is "musixdoc.pdf")' doesn't when run from Terminal? – slhck – 2013-01-01T14:58:32.873

Am I sure the filename is the correct one ? Well, the tab completer in Terminal thinks it is. And it is exactly as you describe, the first osascript works and the second doesn't. I also tried putting the complete path in place of just "musixdoc.pdf", but that fixed nothing. – Ewan Delanoy – 2013-01-01T15:10:35.830

Are you sure the window's name is actually "musixdoc.pdf" and not "Musix Doc" or something else? A PDF might have a different title than its file name. AppleScript looks for the window's title as displayed in the window's top bar, not the actual name or path of the file that's opened (although of course images are titled like their files). This means the title could be Musix Doc.pdf (Page 1 of 9) or similar. – slhck – 2013-01-01T15:19:11.957

On the window’s top bar I read "musixdoc.pdf (page 1 of 121)". Is there a way to pass from the Unix filename to the Preview title programmatically, or vice versa? – Ewan Delanoy – 2013-01-01T15:23:02.787

Got it. Updated my answer again. You need to iterate through the windows and compare the filename passed to the script with the document name of each window. So you could simply call cw Preview musixdoc.pdf. – slhck – 2013-01-01T15:31:21.560

It works fine now! Thank you very much for your time. – Ewan Delanoy – 2013-01-01T15:35:27.200

No worries, happy to help. Welcome to Super User, by the way :) – slhck – 2013-01-01T15:37:07.957

0

The answer by slhck looks good and thorough. Beware, executing the third code line:

sudo codesign -f -s - /Applications/Preview.app

'seems' to have caused Preview to crash on every launch with the following:

Application Specific Information:
XPC domain creation failed: The code signature is not valid: The operation couldn’t be completed. (OSStatus error -67061.)

According to the codesign manual, that -f is forcing 'code sign to replace and existing signature' and the -s is to 'sign the code at the path given' … in this case -

Apparently, Preview is now incorrectly signed and unusable. :(

David C

Posted 2013-01-01T09:55:49.653

Reputation: 101