My script saves a file and can be run multiple times in one session. After the process is done the user is prompted to save the output text (containing the STDOUT output of the cli program I’m wrapping).
If a file by that name already exists, the user is prompted on whether they wish to overwrite the file. If they select “Ok” this error is thrown for a file of name output.txt:
File Macintosh HD:USERS:jkyle:Desktop:output.txt is already open. (-49)
Here is the pertinant code:
on saveOutput(theOutput)
set thePath to choose file name with prompt "Save Output to File" default name "output.txt"
set theFileID to open for access thePath with write permission
write (theOutput as text) & return to theFileID
close theFileID
end saveOutput
Where theOutput is just a text string. As you can see, I do close the file.
Further, the error is produced across sessions of the program. So I can Quit, recompile, save output, and get the same error.
And last, the file does not have to be created by the program (thought the close theFileID might not be working). I can go to Terminal.app and touch path/to/file/output.txt then run my program and try to overwrite it…still same error.
I’ve also checked to make sure that the output.txt was indeed left untouched/unoverwritten to ensure that it was not in fact overwriting it and the error came from some later, overlooked process.
You should try and close the file if an error occurs (which may have happened earlier):
on saveOutput(theOutput)
choose file name with prompt "Save Output to File" default name "output.txt"
try
set theFileID to open for access (result) with write permission
write (theOutput as text) & return to theFileID
close theFileID
on error errMsg number errNum
try
close theFileID
end try
error errMsg number errNum
end try
end saveOutput
Building target “ImageConvertor” of project “ImageConvertor” with configuration “Release” ” (1 error)
cd /Users/jkyle/Projects/Cocoa/ImageConvertor
/usr/bin/osacompile -d -i /System/Library/Frameworks/AppleScriptKit.framework -U ImageConvertor.applescript -x -o /Users/jkyle/Projects/Cocoa/ImageConvertor/build/Release/ImageConvertor.app/Contents/Resources/Scripts/ImageConvertor.scpt /Users/jkyle/Projects/Cocoa/ImageConvertor/ImageConvertor.applescript
/Users/jkyle/Projects/Cocoa/ImageConvertor/ImageConvertor.applescript:255: Expected “end” but found “on”. (-2741)
/Users/jkyle/Projects/Cocoa/ImageConvertor/ImageConvertor.applescript:255: Expected “end” but found “on”. (-2741)
/Users/jkyle/Projects/Cocoa/ImageConvertor/ImageConvertor.applescript:255: Expected “end” but found “on”. (-2741)
Build failed (1 error)
Can’t nest two on blocks. Is there another way to do it?
You can’t have a handler inside a handler, but a try block is not handler. Let’s see the rest of your code (specifically, a few lines before line 255).
Sure thing, this is my first applescript so it’s probably pretty nasty. Haven’t updated the header info yet either…but here you go. You’ll fiind the saveOutput function toward the bottom:
-- ImageConvertor.applescript
-- ImageConvertor
-- DESCRIPTION
-- Simple gui wrapper for mri_convert (part of the freesurfer suite).
-- Expected Input:
-- Either a Directory or a File to be converted
-- As well as any non-required options desired
-- Expected Output for Directory Input:
-- A series of image files of specified $type
-- for each DICOM series found in $directory and
-- $subdirectories
-- Functions:
-- saveOutput(string theOutput)
-- Expected Input: String
-- Expected Output: TextEdit document with content = string theOutput
-- Description: Is passed a $string holding the output of mri_convert
-- Pass $string to TextEdit which creates a new document
-- with property text set to $string
-- getExtensions(string TYPE)
-- Expected Input: String containing value of inputType or outputType
-- Expected Output: string containing extension expected by mri_convert
-- Created by James Kyle on 6/29/06.
-- Copyright 2006 UCLA Brainmapping Center. All rights reserved.
--
property panelWindow : missing value
global mriWrapper
global inputFile
global outputFile
global itemInfo
global FREESURFERHOME
global OPTS
global extKey
global browseButton
on awake from nib
set mriWrapper to (resource path of main bundle) & "/mriWrapper.pl"
set ASTID to AppleScript's text item delimiters
set AppleScript's text item delimiters to {" "}
set textItems to text items of mriWrapper
set AppleScript's text item delimiters to {"\\ "}
set mriWrapper to textItems as string
set AppleScript's text item delimiters to ASTID
set FREESURFERHOME to (resource path of main bundle) & "/freesurfer"
set extKey to title of current cell of matrix "outputType" of drawer "drawer" of window "main"
set OPTS to ""
end awake from nib
on launched theObject
end launched
on choose menu item theObject
if (the name of theObject) is equal to "openFile" then
selectInput()
else if (the name of theObject) is equal to "preferences" then
display alert "This feature has not been enabled as of yet."
(* if panelWindow is equal to missing value then
load nib "SettingsPanel"
set panelWindow to window "settings"
display panelWindow
end if
tell panelWindow
end tell *)
end if
end choose menu item
on clicked theObject
if (the name of theObject) = "selectExt" then
tell window "main"
tell drawer "drawer"
set currentState to state of it
if currentState is in {drawer opened, drawer opening} then
close drawer
else if currentState is in {drawer closed, drawer closing} then
open drawer on bottom edge
end if
end tell
end tell
else if (the name of theObject) = "convert" then
set outputFile to contents of text field "outputFile" of window "main"
if (kind of itemInfo) is "Folder" then
set OPTS to OPTS & "-ot:" & (title of current cell of matrix ¬
"outputType" of drawer "drawer" of window "main") & ":"
set outputFileRef to POSIX file outputFile
set folderName to (do shell script "date '+%H-%M-%S'")
set folderName to "Results-" & folderName
tell application "Finder"
make new folder at outputFileRef with properties {name:folderName}
end tell
set outputFile to outputFile & folderName
end if
set commandString to mriWrapper & " '" & (resource path of main bundle) & "/Libs' " & ¬
" -in " & "'" & inputFile & "'" & ¬
" -out " & "'" & outputFile & "'" & ¬
" -fh " & "'" & FREESURFERHOME & "'"
if ((kind of itemInfo) is "Folder") then
set commandString to commandString & " -oe " & getExtension(extKey)
set Type to " -type directory"
else if ((kind of itemInfo) is not "Folder") then
set Type to " -type file"
end if
set commandString to commandString & Type
if (OPTS is not equal to "") then
set commandString to commandString & " -opts " & "'" & OPTS & "'"
end if
with timeout of 3600 seconds
set theOutput to (do shell script commandString & " 2>&1")
end timeout
saveOutput(theOutput)
set outputFile to ""
set OPTS to ""
else if (the name of theObject) is equal to "outputType" then
set extKey to title of current cell of matrix "outputType" of drawer "drawer" of window "main"
if ((kind of itemInfo) is not equal to "Folder") then
my setOutputExtension()
end if
else if (the name of theObject) is equal to "apply" then
else if (the name of theObject) is equal to "inputBrowse" then
selectInput()
else if (the name of theObject) is equal to "outputBrowse" then
selectOutputDirectory()
end if
end clicked
on panel ended theObject with result withResult
if withResult is 1 then
if browseButton is equal to "input" then
set inputFile to ""
set itemInfo to ""
set inputFile to (item 1 of (path names of open panel as list))
set itemInfo to info for (POSIX file inputFile)
set ASTID to AppleScript's text item delimiters
set AppleScript's text item delimiters to {"/"}
set theFileParent to (text items 1 thru -2 of inputFile) as string
set AppleScript's text item delimiters to ASTID
tell window "main"
set contents of text field "inputFile" to inputFile
if (kind of itemInfo) is "Folder" then
set contents of text field "outputFile" to theFileParent & "/"
set visible of text field "message" to true
show
else
set contents of text field "outputFile" to theFileParent & "/out." & my getExtension(extKey)
set visible of text field "message" to false
show
end if
end tell
set browseButton to ""
else if browseButton is equal to "output" then
set outputDirectory to ""
set outputDirectory to (item 1 of (path names of open panel as list)) & "/"
tell window "main"
if ((kind of itemInfo) is not equal to "Folder") then
set outputDirectory to outputDirectory & "out." & my getExtension(extKey)
end if
set contents of text field "outputFile" to outputDirectory
end tell
set browseButton to ""
end if
end if
end panel ended
on setOutputExtension()
set outputName to contents of text field "outputFile" of window "main"
set ASTID to AppleScript's text item delimiters
set AppleScript's text item delimiters to {"/"}
set rootFile to (text item -1 of outputName) as string
set filePath to (text items 1 thru -2 of outputName) as string
if rootFile is equal to "" then
display alert "You must provide a file name"
else
set AppleScript's text item delimiters to {"."}
if ((count of text items of rootFile) > 1) then
if ((text item -2 of rootFile) is equal to "nii" and (count of text items of rootFile) > 2) then
set fileName to ((text items 1 thru -3 of rootFile) as string) & "." & getExtension(extKey)
else
set fileName to ((text items 1 thru -2 of rootFile) as string) & "." & getExtension(extKey)
end if
else
set fileName to rootFile & "." & getExtension(extKey)
end if
set contents of text field "outputFile" of window "main" to filePath & "/" & fileName
end if
set AppleScript's text item delimiters to ASTID
end setOutputExtension
on getExtension(Type)
set extMap to ¬
{"cor", "cor", ¬
"mgh", "mgh", ¬
"mgz", "mgz", ¬
"minc", "mnc", ¬
"analyze", "img", ¬
"analyze4d", "img", ¬
"spm", "img", ¬
"afni", "brik", ¬
"bshort", "bshort", ¬
"bfloat", "bfloat", ¬
"outline", "mgh", ¬
"gdf", "gdf", ¬
"nifti1", "img", ¬
"nii.gz", "nii.gz"}
set i to 1
repeat until (item i of extMap) is equal to Type
set i to i + 1
end repeat
return (item (i + 1) of extMap)
end getExtension
on saveOutput(theOutput)
choose file name with prompt "Save Output to File" default name "output.txt"
try
set theFileID to open for access (result) with write permission
write (theOutput as text) & return to theFileID
close theFileID
on error errMsg number errNum
try
close theFileID
end try
error errMsg number errNum
end try
end saveOutput
on selectInput()
set can choose directories of open panel to true
set can choose files of open panel to true
set browseButton to "input"
display open panel in directory (system attribute "HOME") attached to window "main"
end selectInput
on selectOutputDirectory()
set browseButton to "output"
set can choose directories of open panel to true
set can choose files of open panel to false
display open panel in directory (system attribute "HOME") attached to window "main"
end selectOutputDirectory
You’ll notice a shell script is called, it’s a perl script I wrote as a wrapper to mri_convert.
So, I’m still getting the save file error. Even using the try code block suggested.
It appears that this is the process erroring out on the second save attempt:
set theFileID to open for access (result) with write permission
So, here’s the issue:
This completes with zero errors on first attemp:
choose file name with prompt “Save Output to File” default name “output.txt”
try
set theFileID to open for access (result) with write permission
write (theOutput as text) & return to theFileID
close theFileID
Then on second attempt this call errors out with "File Macintosh HD:path:output.txt (-49):
set theFileID to open for access (result) with write permission
So the catch all to prevent the problem here:
on error errMsg number errNum
try
close theFileID
Is never being called since the first is not initially throwing an error, even though it is not closing the file.
Is there not some way to simply test if a file is open? So:
choose file
if (file is open) close file
open file for writing
…
In your second attempt, what is ‘result’? It certainly isn’t whatever it was before, and after closing a file, fileID is discarded as well.
Why are you opening and closing the file if you want to write more than once? Just leave it open. If you want to overwrite it, then ‘set eof of fileID to 0’ will accomplish that. If you leave the eof where it was, the second write to the file will append to it. Further, you don’t need a try block to span the whole thing. With each attempt to write to it, you enclose that in a try that will close it if the write doesn’t succeed and displays a dialog of the error that caused it
-- stuff happens
set fid to open for access ((path to desktop as text) & "myFile.txt") with write permission
try
set eof of fid to 0
write ("This is the first entry" & return) to fid -- just a sample
on error e
close access fid
display dialog e
return -- quit; something is screwed up
end try
--- do more stuff; file should still be open, fid still ok if you got here
try -- leaving eof where it was
write "This is the second entry" & return to fid -- second line
on error e
close access fid
display dialog e
return -- bag it
end try
-- continue with stuff
close access fid