Question

I am trying to automate three or four bash shell scripts using AppleScriptObjC as a wrapper. This will give me a friendly gui front end to select data files etc., and the convenient text file manipulating and processing of several bash scripts.

Everything works (I can press my buttons and run the shell scripts) except I can't figure out how to make things semi-portable. I would like to have a single app or package that I could distribute to others, with the needed script files somehow incorporated or generated by the app, rather than exposed to the users.

I am a noob to Xcode and AppleScriptObjC (ASOC) and I can't see the way to do this. - I can run the scripts as set of simple do shell script commands, or through commands sent to a terminal window, but that means all the individual shell files have to be distributed and put it the right place as a bunch of pieces. - I have tried to generate the script files from within the ASOC, but I run into barriers constructing the files due to the way that ASOC handles strings and special characters. I have tried both normal strings or the quoted form of string construction. As background, Apple Technical Note TN2065 is relevant. Creating the files that way loses the special characters. This code demonstrates the issue (e.g., what I am doing wrong!):

set scriptName to "/Users/me/Desktop/myScript.sh" -- Name of file
-- Define a multi line string:
set scriptContents to "       First Line - OK  
Second Line, all good so far
$Test fails - the (dollar sign)Test vanishes
\"$Test\" also fails
Last line"
do shell script ("cd " & myFolder & "; echo \" " & scriptContents & " \" > " & scriptName)

Without a way to create the needed shell scripts from within the ASOC or access the scripts if included in the application bundle as a supporting file, I will be doing a lot of tricky porting from bash to AppleScriptObjC!

Suggestions or solutions would be gratefully accepted: 1. How can I write arbitrary multi-line files with AppleScriptObjC including control characters, etc. 2. or 3. How can I package a shell script so that ASOC can run it when needed?

Was it helpful?

Solution

Answering my own question, after some hours of research and experimentation (with thanks to various posts on macscripter.net and autodidaktos.com):

  1. Add the script file, e.g., yourScriptFile.sh, to the project via the menu File - Add Files to (yourProject)...

  2. Include the script file in the app bundle so that it will be included with the app when distributed:

    • In the Project Navigator or the Jump Bar select your project
    • Select the "Build Phases" tab
    • Expand the "Copy Bundle Resources" section (this applies to Xcode 4.6.3, maybe different with other versions)
    • Add an item to the Bundle Resources by clicking the "+" button at the bottom of the list
    • Select the script file to add to the bundle, e.g., yourScriptFile.sh
  3. In your AppleScriptObjC script, add the following subroutine

    on applicationWillFinishLaunching_(aNotification)
        set pathToResources to (current application's class "NSBundle"'s mainBundle()'s resourcePath()) as string
    end applicationWillFinishLaunching_
    
  4. Also in your AppleScriptObjC script, add the following where appropriate:

    property pathToResources : "NSString"  -- works if added before script command
    
  5. Where appropriate, also add the following in your AppleScriptObjC script:

    set yourScript to pathToResources & "/yourScriptFile.sh" 
    -- gives the complete unix path
    -- if needed, you can convert this to the Apple style path:
    set yourScriptPath to (((yourScript as text) as POSIX file) as alias)`
    
  6. As an aside, you could then open the file for read using

    tell application "Finder"
        open yourScriptPath
    end tell
    

It should then be possible to execute the script or copy it to somewhere for user access using the do shell script construction. It should also be possible to execute the script directly from the bundle if appropriate.

OTHER TIPS

This takes into consideration the following facts:

firstScript = variable name that points to a script called scriptNumberOne.sh
scriptNumberOne.sh = the script that I have embedded into my application to run
ButtonHandlerRunScript_ = the name of the Received Action in Xcode
pathToResources = variable that points to the internal Resources folder of my application, regardless of it's current location

Using this information, below is a copy of a vanilla AppDelegate.applescript in my AppleScriptObjC Xcode project:

script AppDelegate
property parent : class "NSObject"
property pathToResources : "NSString"

on applicationWillFinishLaunching_(aNotification)
    set pathToResources to (current application's class "NSBundle"'s mainBundle()'s resourcePath()) as string
end applicationWillFinishLaunching_

on ButtonHandlerRunScript_(sender)
    set firstScript to pathToResources & "/scriptNumberOne.sh"
    do shell script firstScript
end ButtonHandlerRunScript_

on applicationShouldTerminate_(sender)
    -- Insert code here to do any housekeeping before your application quits 
    return current application's NSTerminateNow
end applicationShouldTerminate_

end script

Perhaps it's different for shell scripts, but for a perl script you can do this with far fewer steps.

Did it just now as follows, in XCode 6 (it's the same in 5):

  1. Add the script file: File > Add files to [your-project-name]

  2. Reference the file wherever you need to, just by:

    (path to resource "myAwesomeScript.pl")

That's it!

Now as you might expect, that returns a Finder style path, so of course you'll have to do the usual 'quoted form of POSIX path of' acrobatics to get a UNIX path and escape the quotes and spaces, but that's all as per usual..

For reference, here's the actual handler I ended up with. Only changed the function and script names to protect the innocent ;)

on doCoolStuffWithLogsFrom(theThingName)
    set theLogsFolder to POSIX path of ((path to home folder as string) & "Library:Logs:Something:Something:" & theThingName)
    tell me to log "Processing logs in: " & theLogsFolder
    do shell script "/usr/bin/perl " & (quoted form of POSIX path of (path to resource "doCoolStuffWithLogs.pl")) & " " & quoted form of theLogsFolder

end doCoolStuffWithLogsFrom

Couple of small things that caught me out:

  • don't forget to add the space between .pl and the script parameters (& " " &)

  • the last 'quoted form of' is where it is so that the Console log line outputs an unescaped UNIX path. Just my preference, for readability. You might prefer it the other way, i.e. put quoted form of in the 'set theLogsFolder' line, so that the log output is readily copy/paste-able into terminal commands.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top