How do I close a window from an application, passing the file name?
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?
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.