Best way to return a record/dictionary from an external shell script

I have an external command/shell script which analyses some XML files and collects a lot of information in a dictionary-type of structure { key1: value1, key2: value2, … }.

In Applescript I can call the tool via a line like

set theInfo to do shell script "/path/to/my/tool"

The shell script can output its data in whatever way is convenient for the applescript

What is the best way to pass the data from the shell script to the Applescript, so that in the Applescript I receive the data as a normal record { key1: value1, key2: value2, … }

Can I somehow make use of the do shell script’s as typeclass parameter?

Format the output of the shell script as a regular record: {x:“y”, z:23} and use run script on that output. Voilá a proper record!

set theInfo to do shell script "/path/to/my/tool"

set myInfo to run script theInfo 

I needed to do this with running a python script.
I ended up using AscOBJC

Using a NSTask, NSPipe, NSFilemanager and NSNotificationCenter

You assign the NSPipe to the task.
NSFileMan and Notifcation center to get notifications
From the NSPipe file.

On notification Capture the new data from the pipe.
Convert the NSData to a string.
Append the sting to a masterString.

When the output is done (a NSNotifcation)
Take the final masterString.
Convert to NSData.
Create a JSON object with the data.
Create a NSDictionary from the JSON object

Thanks for the replies! Both make sense.

However because my keys are determined at runtime I have decided call the external program multiple times, each for each key, then I can just return individual string values.

If the performance is a problem I might do caching in the external program.

Your data to JSON → NSDictionary → AS Record :slight_smile:

It can be done with a plist file as well, especially if the values aren’t complex nested collections, in which case this approach becomes exponentially more difficult.

Let’s say that the shell script has the following keys and values to be sent to the AppleScript script:

Key → Value:
intVal → 1
realVal → 2.2
boolVal → YES (valid boolean values are YES or TRUE, and NO or FALSE; the strings are case-insensitive)
stringVal → “Foo”
listVal → 3, -4.4, NO, “Bar”
record value → intVal:5, realVal:6.0, boolVal:TRUE, stringVal:“Foo Bar”

In the shell script:

(1) Create a plist file with the defaults command, and initialize any arrays and dictionaries with empty values:

defaults write /tmp/ShellValuesRecord.plist '{listVal = (); "record value" = {};}'

NOTES:
- The top-level plist value is always a dictionary with key-value pairs
- The top-level dictionary must have at least one key-value pair; if there are no collection values to code with empty initial values, a dummy key-value pair can be created (and later ignored), or a non-collection key-value pair can be created and replaced via plutil later if necessary
- Plist arrays (analogous to AppleScript lists) are enclosed in parentheses. Array value access is 0-based (0 for the 1st item, 1 for the 2nd item, ...). If arrays are going to be hard-coded in the [i]defaults[/i] command, values are separated by commas; a comma following the final value is optional
- Plist dictionaries (analogous to AppleScript records) are enclosed in curly braces. If dictionaries are going to be hard-coded in the [i]defaults[/i] command, a key and its value are separated by an equal sign, and each key-value pair, including the final pair, must be followed by a semicolon
- Any plist file location will do; it must have a .plist extension

(2) Add values to the plist by key and value with the plutil command:

plutil -insert intVal -integer "1" /tmp/ShellValuesRecord.plist
plutil -insert realVal -float "2.2" /tmp/ShellValuesRecord.plist
plutil -insert boolVal -bool "YES" /tmp/ShellValuesRecord.plist
plutil -insert stringVal -string "Foo" /tmp/ShellValuesRecord.plist
plutil -insert listVal.$((iListVal++)) -integer "3" /tmp/ShellValuesRecord.plist
plutil -insert listVal.$((iListVal++)) -float "-4.4" /tmp/ShellValuesRecord.plist
plutil -insert listVal.$((iListVal++)) -bool "NO" /tmp/ShellValuesRecord.plist
plutil -insert listVal.$((iListVal++)) -string "Bar" /tmp/ShellValuesRecord.plist
plutil -insert "record value".intVal -integer "5" /tmp/ShellValuesRecord.plist
plutil -insert "record value".realVal -float "6" /tmp/ShellValuesRecord.plist
plutil -insert "record value".boolVal -bool "TRUE" /tmp/ShellValuesRecord.plist
plutil -insert "record value".stringVal -string "Foo Bar" /tmp/ShellValuesRecord.plist

NOTES:
- "replace" may be used instead of "insert" to replace existing non-collection values; unfortunately, "replace" inserts rather than replaces when used with arrays and dictionaries
- Dot notation is used to access array and dictionary items
- Insertion into an array must not be at a location greater than the current length of the array, or an out-of-bounds error will occur. To add to the end of the array, use index 0 for the 1st insertion, 1 for the 2nd insertion, etc. $((indexVal++)) is a concise way of automatically starting at index 0 (since the arithmetic value of an undefined variable is 0), and then incrementing with each subsequent insertion; ++ must follow rather than precede the index variable name so that its current value is used for indexing prior to its incrementation for the next insertion. If an index of 0 is used for all insertions, each new item will be added to the beginning of the array, and the array will have to be reversed to restore the original order.
- Values are treated as strings by default both in the shell script and in subsequent AppleScript access, unless they are explicitly declared to be another type with the -integer, -float, -bool, etc, ... option
- Values containing spaces, decimal points, and other special characters, must be enclosed in quotes; I find it easiest always to quote values
- Keys containing spaces, decimal points, and other special characters must also be enclosed in quotes; they will become piped record labels when retrieved in the AppleScript script (I purposely included "record value" as an example of a key with a space character)

Now comes the fun part. Issue the following command in the AppleScript script:

tell application "System Events" to set shellValuesRecord to property list file "/tmp/ShellValuesRecord.plist"'s value as record
-->
{
	realVal:2.2, 
	boolVal:true, 
	stringVal:"Foo", 
	|record value|:{
		realVal:6.0, 
		boolVal:true, 
		stringVal:"Foo Bar", 
		intVal:5
	}, 
	listVal:{
		3, 
		-4.4, 
		false, 
		"Bar"
	}, 
	intVal:1
}