0

If I create an AppleScript script called myscript.js and pass it to oascript it will execute the run function once and write "hello world" to standard data out:

function run(args) {

  for (var i=0;i<10;i++) {
     // out("number " + i); // execution error: Error on line 18: ReferenceError: Can't find variable: out (-2700)
  }

  return "hello world"; // written to standard data out
}

But if I want to write to standard data out multiple times, for example, in the for loop, how would I do this?

If I do the following it writes to standard error out multiple times and dispatches multiple events in the external application:

  for (var i=0;i<10;i++) {
     console.log("number " + i);
  }

The only problem is that it's creating error events not standard data events.

In Script Editor the standard data out is sent to the results window. I want to print to the results window multiple times. From Mac OSX Script Editor

UPDATE:
It looks like it might not be possible. I found this quote here:

Q: My script will produce output over a long time. How do I read the results as they come in?

A: Again, the short answer is that you don’t — do shell script will not return until the command is done. In Unix terms, it cannot be used to create a pipe. What you can do, however, is to put the command into the background (see the next question), send its output to a file, and then read the file as it fills up.

Also, side note, if I want to use JavaScript instead of AppleScript should I be using cocoascript instead of osascript?

1.21 gigawatts
  • 16,517
  • 32
  • 123
  • 231
  • I think you’re getting yourself confused between Unix-to-OSA (`osascript`) and OSA-to-Unix (`do shell script`) communication. Your original question asks how to achieve the former—i.e. how to make an AS/JS shell process write data to stdout—whereas your newly added footnote is only relevant to the latter. e.g. In Node, you typically call [`console.log(…)`](https://millermedeiros.github.io/mdoc/examples/node_api/doc/stdio.html#console.log) to print a message to stdout and `console.error(…)` to print to stderr, or use [`process`](https://nodejs.org/api/process.html) if you need greater control. – foo Jan 12 '18 at 21:34
  • The footnote says that I can't send data to `standard data out` until the command is done and through `return`. I don't know if that is correct because if I am in Script Editor and I use console.log() and attach it to Safari, it sends output to the Safari console. But if I run it in my Adobe AIR console.log sends data to `standard error out`. If I `return` a value from the script, that value is sent to `standard data out`. – 1.21 gigawatts Jan 12 '18 at 22:45
  • Forget Script Editor. It’s an OSA environment, not a Unix one, so has NO understanding of—or support for—stdout/stderr/etc. Get yourself a decent plain text code editor (e.g. I use BBEdit), put `#!/usr/bin/osascript -l JavaScript` at the top of your script, and run it as a Unix shell script. You can then use `NSFileHandle.fileHandleWithStandardOutput().writeData(…)` to write directly to stdout, bypassing the limitations imposed on OSA. But again, it’s a lot of hassle when the best answer is to *avoid OSA completely*, and use the right tool for the job (Python/Ruby/Node/etc) in the first place. – foo Jan 13 '18 at 11:25
  • Thanks. You mention JavaScript but then you mention NS something or other. I'm guessing Objective C. I will have to research what you said. For now I have my osascript working after 3 days. I will live without standard data out for now or use one of the hacks. – 1.21 gigawatts Jan 14 '18 at 06:14
  • `NS` is the traditional prefix for Cocoa class names as they appear in ObjC and most ObjC-bridged languages (Swift is an obvious exception). If you’re going to use Cocoa APIs then you *really* need to familiarize yourself with ObjC syntax before you can find your way around. If you don’t need to use Cocoa or Apple events, there’s no real value in JXA other than [making your life difficult](https://weblogs.asp.net/alex_papadimoulis/408925); either way there are much better options available: Swift for Cocoa development, AppleScript for desktop automation, Node for everything JavaScript. – foo Jan 16 '18 at 16:29

2 Answers2

2

“Also, should I be using cocoascript instead of osascript?”

TL;DR: If you love JavaScript, neither. Go Node!


Long version:

JXA (JavaScript for Automation) is buggy and half-baked, with virtually no user documentation, tools, libraries, or community. The Apple team responsible for delivering it was officially disbanded and reassigned/fired in 2016, and the entire macOS Automation platform stuck in maintenance mode (and already bitrotting!) since 10.13. After a quarter-century of persistent neglect, mismanagement, and screwups, it doesn’t take an expert to guess where Apple’s legacy, Mac-only, Automation technologies are headed now.

CocoaScript works…and that’s about it. It’s third-party open-source software, so at least it’s not reliant on Apple for its continued development and support, but it’s never grown a sufficiently large and vigorous user community to make it a popular success. And a quick glance at the CocoaScript/Mocha projects on GitHub likewise indicates no ongoing development beyond essential maintenance.

This is not to say you can’t use them […for now], but unless you’ve a unavoidably compelling reason to do so there is only one JS platform that matters now: Node.js.

Unlike the above, Node enjoys enormous global investment, development, tooling, documentation, community, and market growth. (3.5 MILLION users in 2016 and growing!) Fully open-source and independent. Runs on nearly every OS platform that matters too: Windows, Mac, Linux; even Android. NPM is a phenomenal resource too: easily up there with PyPI, RubyGems, &co. (Even has macOS libraries for Cocoa and Apple events, though both may require some TLC right now due to all the chop and uncertainty in Apple’s increasingly chaotic platform.) Oh, and massive job and FOSS project opportunities, should you someday wish to turn pro too.

HTH


p.s. To answer your original question, use -[NSFileHandle fileHandleWithStandardOutput] to get a pipe to stdout and invoke its -writeData: method as many times as you like, passing (e.g.) an NSData instance created via [[NSString stringWithString: aMessage] dataUsingEncoding: NSUTF8StringEncoding] as it argument. Or, y’know, just ignore all that and Node it! ;)

/relurk

has
  • 167
  • 2
  • So if I want to use node instead I would install `node` and then pass a script to `node` instead of `osascript`? Is `node` preinstalled on OSX? If not is there any chance it will be preinstalled in the future? – 1.21 gigawatts Jan 12 '18 at 08:27
  • FYI I don't understand your code in the answer. It's Obj-C? – 1.21 gigawatts Jan 12 '18 at 08:29
  • 1
    1. Yup, type `node` in Terminal to start an interactive session, or `node yourscript.js` to run a JS script in Terminal, or add `#!/usr/bin/env node` at the top of your script file and `chmod +x yourscript.js` to make it executable. 2. It’s not preinstalled and unlikely to be as Apple doesn’t (and can’t) control it; however, installing it yourself is trivial (standard point-n-click macOS pkg installer) and the rewards are huge. – has Jan 12 '18 at 17:49
  • To use Cocoa in JavaScript/Python/Lua/etc you’ll have to learn how to translate its documentation to native syntax yourself; the Cocoa API is vast and Apple writes its documentation and example code for ObjC and Swift users only. Each bridge maps ObjC features and method names to your own language slightly differently, so learn the bridge’s own documentation too. p.s. For serious Cocoa development, best to suck it up and learn Swift: the language is a bloaty mess and its Cocoa bridge as fiddly as the rest, but it’s the one option *guaranteed* to enjoy massive Apple investment and support. – has Jan 12 '18 at 18:07
0

A script is an expression which evaluates to a final value, returned to the caller from the JSContext.

If you want that value to consist of repeated or multiple lines, then that is the value which your script needs to define and return.

(() => {

    // enumFromToInt :: Int -> Int -> [Int]
    const enumFromToInt = (m, n) =>
        n >= m ? Array.from({
            length: Math.floor(n - m) + 1
        }, (_, i) => m + i) : [];

    // unlines :: [String] -> String
    const unlines = xs => xs.join('\n');

    return unlines(
        enumFromToInt(1, 25)
        .map(n => n.toString() + " hello")
    );
})()
houthakker
  • 688
  • 5
  • 13