AppleScript and standard input

What I like most about Unix is its depth. There’s always something more to learn. This morning, that highly sought after podcast guest Gabe Weatherhead linked to a cheat sheet by Peteris Krumins that summarized the input and output redirections available in bash. Most of them were familiar to me, but one made me sit up and take notice: a way of turning a string on the command line into the standard input for a command.

This is a useful trick when writing AppleScripts that use do shell script to run Unix utilities. AppleScript itself has no mechanism for creating standard input to feed to the commands do shell script calls. Apple, in this technical note, suggests using the echo command, which is what I did when writing my bbstdin utility a couple of weeks ago. But what I learned from the redirection cheat sheet, and from this earlier article, also by Peteris Krumins, was a more compact method.

The operator that does the conversion is <<<. It’s a relative of the more familiar (to me, at least) << operator. The << operator creates what are known as here documents, a way of creating a temporary, multi-line file-like object “right here.” Here documents are common in shell scripting, and the notation was adopted by Perl and other languages as a standard way of creating multi-line strings. The <<< variant is sometimes called a here string. It’s main use in shell scripting seems to be in creating single-line here documents.

But in AppleScript, it can be put to a more general use. For example,

applescript:
set a to "abcdef
ghijk
lmonp
qrstu
vwxyz"
do shell script "wc <<< " & quoted form of a

will yield

      5       5      31

which, if you’re not familiar with the output of the wc command, means “5 lines, 5 words, and 31 characters.”1 This is, I think, easier to understand than the equivalent

applescript:
set a to "abcdef
ghijk
lmonp
qrstu
vwxyz"
do shell script "echo " & quoted form of a & " | wc"

because it puts the command you really want to run at the beginning of the command string instead of burying it at the end.

One thing you might need to be careful with in using <<< is that it adds a linefeed to the end of the string. In the example above, there are actually only 30 characters in the original string, the 26 letters and 4 linefeeds. An easier example in which to see this is

applescript:
set b to "abcde"
do shell script "xxd <<< " & quoted form of b

The xxd command does a hex dump of its input, which in this case yields

0000000: 6162 6364 650a                           abcde.

The hex dump is in the middle section, after the colon. The 0a at the end of the hex dump is the ASCII code for the linefeed character, which was added by <<<.

If you’re a Perl programmer, you might wonder whether <<< is sort of like chomp in reverse—clever enough to add a linefeed only if there isn’t already one there. It isn’t. The output of

applescript:
set b to "abcde" & linefeed
do shell script "xxd <<< " & quoted form of b

is

0000000: 6162 6364 650a 0a                        abcde..

For most of my uses, the extra linefeed won’t matter, but it’s good to know that it’s there.

The advantage of having <<< in your toolbox is that it, like the echo trick, gives your AppleScripts the power of Unix’s text handling utilities as well as any Perl, Python, Ruby, or shell scripts you can write or find.


  1. OK, not characters, bytes. But when the string is all ASCII, as this one is, bytes and characters are interchangeable. 


4 Responses to “AppleScript and standard input”

  1. Doug Adams says:

    Nice!

  2. has says:

    The lack of any stdin support in ‘do shell script’ is ridiculous, but such half-bakedness is depressingly par for the course in the AppleScript world. (Once you use subprocess you never go back.) While I can appreciate the extra challenges of dealing with streams from a Unix-unfriendly language that only does messages, it really didn’t have to be complicated. I can’t think of a situation where an AppleScripter would want to feed input data incrementally (it’s just not how they think or work), so all it really needed was a ‘with input’ parameter that takes a string or data object of any length and throws it at the shell script’s stdin in one go. No need to keep the pipe open beyond that or do anything else fancy.

    As for passing input data via the command string as a workaround, remember that the shell imposes a maximum length limit (it varies with OS version, but is typically something like 256KB), and it won’t work for anything that can’t be represented as UTF8 (i.e. binary data or strings containing ASCII 0 characters). If you can’t be certain your input data falls within those limits, you should write it out to a temp file and have your shell script take that as its input. (If you have the dread book, there’s basic discussion and recipes on p883-5.)

  3. Lauri Ranta says:

    I use <<< all the time, but up until now I thought it only added a linefeed if the text after it didn’t already end with one.

    Another disadvantage of echo is that it interprets escape sequences inside single quotes by default in sh. (The default shell of do shell script is /bin/sh, which is a version of bash that emulates sh according to the tech note linked to in the post.) If the input can contain backslashes, you have to do something like shopt -u xpg_echo; echo -n to preserve them.

    As has mentioned, both methods result in an error if the input is longer than getconf ARG_MAX bytes.

  4. Dr. Drang says:

    has,
    A with input would be wonderful and would be in keeping with common AppleScript nomenclature. Although I work with big text files often, it’s never in AppleScript so I doubt I’ll run into the file size limit. Still, good to know.

    And I do have the dread book and have played around with the temp file recipes. Until I run into trouble, I’ll stick with redirection or echo.

    Lauri,
    I believe this is the first time I’ve written something—even something minor—that you didn’t already know. I may take off work early to celebrate.