How to write to standard data out using JavaScript or AppleScript multiple times?

1.8k views Asked by At

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?

2

There are 2 answers

4
has On

“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

0
houthakker On

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")
    );
})()