# Osascript > Quick reference for osascript command-line usage, options, and patterns. --- # osascript Command-Line Reference Source: https://www.manpagez.com/man/1/osascript/ Source: https://ss64.com/mac/osascript.html Quick reference for osascript command-line usage, options, and patterns. ## Basic Syntax ```bash osascript [OPTIONS] [SCRIPT_SOURCE] [ARGUMENTS] ``` ## Options ### Language Selection ```bash -l LANGUAGE Override language for plain text files (default: AppleScript) Common values: AppleScript, JavaScript ``` Example: ```bash osascript -l JavaScript -e 'console.log("test")' osascript -l AppleScript -e 'display dialog "test"' ``` ### Script Input Methods ```bash -e STATEMENT Execute inline script statement Multiple -e flags concatenate to form multi-line script PROGRAMFILE Read script from file Can be plain text or pre-compiled .scpt Stdin used if no file specified and no -e flags ``` Examples: ```bash # Single -e statement osascript -e 'tell app "Safari" to activate' # Multiple -e for multi-line script osascript -e 'tell application "Finder"' \ -e ' activate' \ -e 'end tell' # From file osascript ~/my-script.applescript # From stdin echo 'display dialog "test"' | osascript ``` ### Output Formatting ```bash -s FLAGS Style flags for output formatting Flags: h Human-readable output (default) - No quotes around strings - No escape sequences - Reader-friendly format s Source form (recompilable) - With quotes and escape sequences - Can be re-parsed as valid code - More precise representation e Errors to stderr (default) o Errors to stdout Instead of to stderr ``` Examples: ```bash # Default: human-readable, errors to stderr osascript -e 'return {1, 2, 3}' # Output: 1, 2, 3 # Source form: recompilable format osascript -s 's' -e 'return {1, 2, 3}' # Output: {1, 2, 3} # String with quotes (source form) osascript -s 's' -e 'return "hello"' # Output: "hello" # String without quotes (human form) osascript -e 'return "hello"' # Output: hello # Errors to stdout instead of stderr osascript -s 'o' -e 'error "test"' ``` ### Interactive Mode ```bash -i Interactive mode Show input/output line by line Useful for debugging scripts ``` Example: ```bash osascript -i << 'EOF' tell application "Finder" activate end tell EOF ``` ## Script Arguments Arguments following the script are passed to the `run` handler as a list of strings. ### AppleScript Arguments ```applescript on run argv log argv -- argv is a list of arguments log item 1 of argv -- First argument end run ``` Usage: ```bash osascript script.applescript arg1 arg2 arg3 # argv = {"arg1", "arg2", "arg3"} ``` ### JXA Arguments ```javascript function run(argv) { console.log(argv) // Array of arguments console.log(argv[0]) // First argument } ``` Usage: ```bash osascript -l JavaScript script.js arg1 arg2 arg3 # argv = ["arg1", "arg2", "arg3"] ``` ## Common Patterns ### One-Liner Commands Activate an application: ```bash osascript -e 'tell app "Safari" to activate' osascript -e 'tell application "Finder" to activate' osascript -e 'tell application "Mail" to activate' ``` Get system information: ```bash osascript -e 'system info' osascript -e 'current date' osascript -e 'system attribute "SystemVersion"' ``` Get clipboard: ```bash osascript -e 'the clipboard' ``` Set clipboard: ```bash osascript -e 'set the clipboard to "new content"' ``` Control volume: ```bash osascript -e 'set volume output volume 50' # Set to 50% osascript -e 'set volume output volume 0' # Mute osascript -e 'set volume output volume 100' # Max ``` Text-to-speech: ```bash osascript -e 'say "Hello world"' osascript -e 'say "test" using "Victoria"' ``` ### Multi-Line Scripts ```bash osascript -e 'tell application "System Events"' \ -e ' keystroke "a" using command down' \ -e 'end tell' ``` With variables: ```bash osascript -e 'set myVar to 42' \ -e 'set myText to "hello"' \ -e 'display dialog myText & ": " & myVar' ``` ### Script Files Create a script file: ```bash cat > my-script.applescript << 'EOF' tell application "Finder" activate set folderContents to every file of the desktop display dialog "Files: " & (count of folderContents) end tell EOF ``` Execute the script: ```bash osascript my-script.applescript ``` With arguments: ```bash osascript my-script.applescript arg1 arg2 ``` ### Shebang Scripts Create an executable script: ```bash #!/usr/bin/osascript tell application "Safari" to activate ``` Or with specific language: ```bash #!/usr/bin/osascript -l JavaScript Application("Safari").activate() ``` Make executable and run: ```bash chmod +x script.applescript ./script.applescript ``` ### JavaScript for Automation (JXA) Inline JXA: ```bash osascript -l JavaScript -e 'Application("Safari").activate()' ``` JXA script file: ```bash #!/usr/bin/osascript -l JavaScript var app = Application("Finder") app.activate() var home = app.home() console.log(home) ``` ### Error Handling Capture error output: ```bash output=$(osascript -e 'some-command' 2>&1) if [ $? -ne 0 ]; then echo "Error: $output" fi ``` Suppress errors: ```bash osascript -e 'command' 2>/dev/null ``` Errors to stdout (not stderr): ```bash osascript -s 'o' -e 'error "test error"' ``` ### Passing Shell Variables ```bash VAR="hello world" osascript -e "display dialog \"$VAR\"" ``` Note: Be careful with special characters. Use single quotes when possible: ```bash VAR="hello" osascript -e 'tell app "Finder" to activate' \ -e "set myVar to \"$VAR\"" \ -e 'display dialog myVar' ``` ## Return Values osascript prints the result of the script to stdout. ### Different Data Types ```bash # Boolean osascript -e 'return true' # Output: true # Number osascript -e 'return 42' # Output: 42 # String (human-readable) osascript -e 'return "hello"' # Output: hello # String (source form) osascript -s 's' -e 'return "hello"' # Output: "hello" # List osascript -e 'return {1, 2, 3}' # Output: 1, 2, 3 # List (source form) osascript -s 's' -e 'return {1, 2, 3}' # Output: {1, 2, 3} # Record osascript -e 'return {name:"test", value:42}' # Output: value:42, name:"test" ``` ### Capturing Output ```bash # Store in variable result=$(osascript -e 'the clipboard') echo "Clipboard: $result" # Parse structured output osascript -s 's' -e 'return {1, 2, 3}' | tr ',' '\n' ``` ## Exit Status - **0** - Script executed successfully - **Non-zero** - Error occurred during execution Example: ```bash osascript -e 'tell app "Safari" to activate' echo $? # Prints 0 if successful ``` Error example: ```bash osascript -e 'error "test"' echo $? # Prints non-zero error code ``` ## Performance Tips 1. **Combine multiple -e flags** rather than calling osascript multiple times: ```bash # Slow (multiple process starts) osascript -e 'tell app "Safari" to activate' osascript -e 'tell app "Safari" to open location "test"' # Fast (single process) osascript -e 'tell application "Safari"' \ -e ' activate' \ -e ' open location "test"' \ -e 'end tell' ``` 2. **Use pre-compiled scripts** for repeated execution: ```bash osacompile -o script.scpt script.applescript osascript script.scpt # Faster than parsing source ``` 3. **Minimize application activation** when possible (slow operation) 4. **Use background-only commands** when UI isn't needed ## Common Scripting Tasks ### Application Control Activate and focus: ```bash osascript -e 'tell app "Safari" to activate' ``` Open URL: ```bash osascript -e 'tell app "Safari" to open location "https://example.com"' ``` Quit application: ```bash osascript -e 'tell app "Safari" to quit' ``` ### File Operations Get home folder: ```bash osascript -e 'path to home folder' ``` Get desktop: ```bash osascript -e 'path to desktop folder' ``` List files: ```bash osascript -e 'tell application "Finder" to list folder (path to desktop folder)' ``` Make new folder: ```bash osascript -e 'tell application "Finder"' \ -e ' make new folder at desktop' \ -e ' with properties {name:"NewFolder"}' \ -e 'end tell' ``` ### User Interaction Simple dialog: ```bash osascript -e 'display dialog "Are you sure?" buttons {"No", "Yes"} default button "Yes"' ``` Get text input: ```bash osascript -e 'display dialog "Enter name:" default answer ""' \ -e 'text returned of result' ``` Alert notification: ```bash osascript -e 'display notification "Message" with title "Title"' ``` ### System Commands Run shell command: ```bash osascript -e 'do shell script "uptime"' ``` With elevated privileges: ```bash osascript -e 'do shell script "sudo command" with administrator privileges' ``` System sleep: ```bash osascript -e 'tell application "System Events" to sleep' ``` Restart computer: ```bash osascript -e 'tell application "System Events" to restart' ``` ## Shell Integration Examples ### Loop with osascript ```bash for i in {1..5}; do osascript -e "display dialog \"Number: $i\"" done ``` ### Conditional Execution ```bash if osascript -e 'tell app "Safari" to activate' 2>/dev/null; then echo "Safari activated" else echo "Failed to activate Safari" fi ``` ### Pipe Data to osascript ```bash echo "Hello from pipe" | osascript -e 'read from stdin' 2>/dev/null || \ osascript -e 'display dialog "Hello"' ``` ### Capture and Process Output ```bash clipboard=$(osascript -e 'the clipboard') if [ -z "$clipboard" ]; then echo "Clipboard is empty" else echo "Clipboard: $clipboard" fi ``` ## Troubleshooting ### "osascript: command not found" osascript is only available on macOS. On Linux/Windows, use a remote Mac or virtual machine. ### "Permission denied" errors Use `sudo` for commands requiring elevated privileges: ```bash sudo osascript -e 'command' ``` ### "User canceled" errors Some dialogs can be cancelled by the user, returning an error. Handle with try/catch: ```bash osascript -e 'try' \ -e ' display dialog "Continue?" buttons {"Cancel", "OK"}' \ -e 'on error' \ -e ' log "User cancelled"' \ -e 'end try' ``` ### Script syntax errors Use Script Editor to test syntax before using in osascript: 1. Open `/Applications/Utilities/Script Editor.app` 2. Paste your script 3. Click "Compile" to check syntax 4. Use "Event Log" to see execution details ### Can't find application Make sure application is installed and properly named: ```bash # Check if app is installed osascript -e 'tell application "Finder" to open location "/Applications"' # Find exact app name ls /Applications | grep -i appname ``` ## Related Tools - **osacompile** - Compile AppleScript to binary format - **osalang** - List available OSA languages - **open** - Open files and applications - **say** - Text-to-speech (not via osascript) - **defaults** - Read/write user preferences - **launchctl** - Control launch agents/daemons --- # osascript - macOS AppleScript and JXA Execution ## Source References - https://developer.apple.com/library/archive/documentation/AppleScript/ - https://ss64.com/mac/osascript.html - https://www.manpagez.com/man/1/osascript/ osascript is the command-line tool for executing AppleScript and JavaScript for Automation (JXA) scripts on macOS. It enables automation of Mac applications and system tasks. ## Overview osascript executes scripts written in AppleScript or JavaScript for Automation (JXA) and is part of the Open Scripting Architecture (OSA) framework. It allows users to: - Automate macOS applications via Apple Events - Combine features from multiple scriptable applications - Create powerful workflow solutions - Reduce errors and save time on repetitive tasks - Control system functions and settings ## Syntax ```bash osascript [-l language] [-s flags] [-e statement | programfile] [argument ...] ``` ## Common Options ### Language Selection - **`-l language`** - Override the default language for plain text files - Default language is AppleScript - Can specify `JavaScript` for JXA scripts - Used when processing plain text files, not compiled scripts ### Script Input - **`-e statement`** - Enter one line of script code - Multiple `-e` flags can be combined to build multi-line scripts - Useful for quick one-liners from shell - Arguments following the script are passed to the run handler - **`programfile`** - Read script from a file - Can be plain text or pre-compiled script - File extension determines processing (if plain text) - Use for larger scripts or reusable script files ### Output Formatting - **`-s flags`** - Modify output formatting using modifier characters: - **`h`** - Human-readable form (default, without quotes or escape sequences) - **`s`** - Source form (recompilable, with escape sequences and quotes) - **`e`** - Errors to stderr (default) - **`o`** - Errors to stdout ### Interactive Mode - **`-i`** - Interactive mode with line-by-line prompts - Shows input/output for debugging - Useful for testing script snippets ## Script Arguments Script arguments are passed to the script's `run` handler as a list of strings. In AppleScript, access them via: ```applescript on run argv -- argv is a list of strings passed from command line log argv end run ``` ## Common Usage Examples ### Simple AppleScript Command ```bash osascript -e 'tell app "Safari" to activate' ``` This brings Safari to the front. ### Multi-line AppleScript ```bash osascript -e 'tell application "System Events"' \ -e 'set volume output volume 50' \ -e 'end tell' ``` ### Display a Dialog ```bash osascript -e 'display dialog "Hello, World!" buttons {"OK"} default button "OK"' ``` Returns the button clicked. ### Get Dialog Input ```bash osascript -e 'display dialog "Enter your name:" default answer "" buttons {"Cancel", "OK"} default button "OK"' \ -e 'text returned of result' ``` ### System Control Examples **Adjust system volume:** ```bash osascript -e 'set volume output volume 75' ``` **Toggle dark mode:** ```bash osascript -e 'tell application "System Events"' \ -e ' tell appearance preferences' \ -e ' set dark mode to not dark mode' \ -e ' end tell' \ -e 'end tell' ``` **Get current date and time:** ```bash osascript -e 'current date' ``` **Get system information:** ```bash osascript -e 'system info' ``` ### Working with Files and Clipboard **Get clipboard contents:** ```bash osascript -e 'the clipboard' ``` **Set clipboard contents:** ```bash osascript -e 'set the clipboard to "Hello World"' ``` **Get file information:** ```bash osascript -e 'info for (path to home folder)' ``` ### Running Shell Commands from AppleScript ```bash osascript -e 'do shell script "ls -la ~"' ``` This executes a shell command and returns its output. ### Using Arguments ```bash osascript -e 'on run argv' \ -e ' display dialog item 1 of argv' \ -e 'end run' \ "Hello from command line" ``` ### Text-to-Speech ```bash osascript -e 'say "Hello, I am speaking to you"' ``` With specific voice: ```bash osascript -e 'say "Hello" using "Victoria"' ``` ### Notification Center ```bash osascript -e 'display notification "Alert message" with title "Title" subtitle "Subtitle"' ``` ## JavaScript for Automation (JXA) JXA is a JavaScript-based scripting interface introduced in OS X 10.10 that provides peer functionality to AppleScript. ### Basic JXA Syntax ```javascript // Simple JXA to activate Safari Application("Safari").activate() ``` ### Running JXA with osascript ```bash osascript -l JavaScript -e 'Application("Safari").activate()' ``` ### JXA Script File Create a script file with shebang: ```bash #!/usr/bin/osascript -l JavaScript // Get Safari URL var safari = Application("Safari") var window = safari.windows[0] var tab = window.currentTab safari.activate() ``` Make executable and run: ```bash chmod +x script.js ./script.js ``` ### Accessing Applications ```javascript // Get application reference var app = Application("Finder") // List volumes app.disks() // Get home folder Application("Finder").home() ``` ### System Events ```javascript // Get system information var sys = Application("System Events") // Check if dark mode is enabled sys.appearancePreferences.darkMode() // List running processes sys.processes.whose({ visible: true })() ``` ### JXA File Operations ```javascript // Read file content var fileContents = $('/path/to/file').contents var text = ObjC.unwrap(fileContents) // Path handling var Path = $.NSString.stringWithString(path) var manager = $.NSFileManager.defaultManager // Check file existence manager.fileExistsAtPath(Path) ``` ### JXA User Interaction ```javascript // Alert dialog var app = Application.currentApplication() app.activate() app.displayAlert('Alert Title', { message: 'This is the message', buttons: ['Cancel', 'OK'], defaultButton: 'OK', cancelButton: 'Cancel' }) // Input dialog app.displayDialog('Enter text:', { defaultAnswer: 'default value', buttons: ['Cancel', 'OK'], defaultButton: 'OK' }) ``` ### Objective-C Bridge JXA can access Objective-C libraries directly: ```javascript // Import frameworks ObjC.import('Foundation') ObjC.import('Cocoa') // Use Objective-C APIs var alert = $.NSAlert.alloc.init alert.messageText = "Title" alert.informativeText = "Message" alert.runModal ``` ## Output Formatting with osascript ### Default Human-Readable Format ```bash $ osascript -e 'return {1, 2, 3}' 1, 2, 3 ``` ### Source Form (Recompilable) ```bash $ osascript -s 's' -e 'return {1, 2, 3}' {1, 2, 3} ``` Useful for capturing output that can be re-compiled. ### Error Redirection Default (errors to stderr): ```bash osascript -e 'error "test error"' 2>/dev/null ``` Redirect errors to stdout: ```bash osascript -s 'o' -e 'error "test error"' ``` ## AppleScript Language Basics ### Tell Statements The `tell` statement targets an application: ```applescript tell application "Finder" -- Finder commands here activate make new folder at desktop end tell ``` ### Variables and Data Types ```applescript set myString to "Hello" set myNumber to 42 set myList to {1, 2, 3, 4} set myRecord to {name:"John", age:30} ``` ### Control Flow **If statements:** ```applescript if x > 5 then display dialog "Greater than 5" else display dialog "Less than or equal to 5" end if ``` **Repeat loops:** ```applescript repeat 5 times beep end repeat repeat with i from 1 to 10 log i end repeat ``` ### Handlers (Functions) ```applescript on myHandler(param1, param2) return param1 + param2 end myHandler myHandler(5, 3) -- Returns 8 ``` ### Working with Properties ```applescript tell application "Finder" set folderContents to every file of the desktop set folderCount to count of folderContents end tell ``` ## Scripting Additions AppleScript has standard scripting additions that provide built-in commands: ### File Operations (Scripting Additions) ```applescript -- Read a file open for access file "/path/to/file" read file "/path/to/file" close access file "/path/to/file" -- Write to a file open for access file "/path/to/file" with write permission write "text" to file "/path/to/file" close access file "/path/to/file" -- File information info for file "/path/to/file" list folder "/path/to/folder" ``` ### User Interaction (Scripting Additions) ```applescript -- Dialogs display dialog "Message" buttons {"Button1", "Button2"} display alert "Alert Title" message "Message text" display notification "Notification text" with title "Title" -- File/Folder selection choose file choose folder choose from list myList -- Color and application selection choose color choose application ``` ### System Commands ```applescript -- Shell execution do shell script "ls -la" -- System information system info current date -- Beep sound beep beep 3 -- Beep 3 times -- Volume control set volume output volume 50 -- Text-to-speech say "Hello world" ``` ### Clipboard ```applescript -- Get clipboard the clipboard -- Set clipboard set the clipboard to "text" -- Get clipboard information clipboard info ``` ## Inter-Application Communication AppleScript uses Apple Events to communicate between applications. Each scriptable application defines its own command vocabulary. ### Common Application Targets ```applescript -- Finder tell application "Finder" make new folder at desktop with properties {name:"New Folder"} reveal file "/path/to/file" delete item end tell -- Safari tell application "Safari" open location "https://example.com" set the URL of document 1 to "https://example.com" tell document 1 to do JavaScript "console.log('test')" end tell -- Mail tell application "Mail" set newMessage to make new outgoing message tell newMessage set sender to "email@example.com" set subject to "Hello" set content to "Message body" send end tell end tell -- System Events tell application "System Events" keystroke "a" using command down -- Command+A click menu item "Copy" of menu 1 of menu bar item "Edit" end tell ``` ## Error Handling ```applescript try -- Code that might error tell application "NonExistentApp" activate end tell on error errMsg number errNum display dialog "Error: " & errMsg & " (Code: " & errNum & ")" end try ``` ## Compiling and Saving Scripts ### Using osacompile ```bash # Compile AppleScript to .scpt osacompile -o script.scpt script.applescript # Run compiled script with osascript osascript script.scpt ``` ### Script Bundles AppleScript can be saved as script bundles (.scptd) containing resources: ```bash # Create script bundle osacompile -o script.scptd script.applescript ``` ## Performance Considerations 1. **Local execution** - osascript runs locally without network overhead 2. **Application activation** - Bringing apps to front is slower than background execution 3. **UI Automation** - UI automation via System Events is slower than direct Apple Events 4. **Script compilation** - Use pre-compiled scripts for repeated execution 5. **Threading** - AppleScript doesn't support multi-threading natively ## Security and Sandboxing - **Script Editor** - Scripts must be approved in System Preferences > Security - **Sandboxed apps** - Limited Apple Event access for sandboxed applications - **Elevated privileges** - Use `sudo osascript` for admin commands - **Credentials** - Never embed passwords in scripts; use Keychain instead ## Related Commands - **`osacompile`** - Compile AppleScript to binary format - **`osalang`** - List available OSA languages - **`open`** - Open files and applications - **`defaults`** - Read/write user defaults - **`launchctl`** - Control launch daemons and agents ## Debugging Tips ### Print Debug Output ```applescript log "Debug message" -- Shows in Script Editor's Event Log display dialog "Debug: " & myVar ``` ### Test in Script Editor 1. Open `/Applications/Utilities/Script Editor.app` 2. Write or paste script 3. Click "Run" button 4. Check "Event Log" tab for results 5. Use "Compile" to check syntax ### Error Messages Most errors include a line number. Use the Script Editor to debug: ```applescript try 1 / 0 -- Will error on error message number n from object partial result pResult log "Error: " & message log "Error Number: " & n log "Object: " & object end try ``` ### Testing with osascript ```bash # Run with error output osascript -e 'tell app "Finder" to activate' 2>&1 # Use source format for debugging osascript -s 's' -e 'return {1, 2, 3}' # Interactive mode osascript -i -e 'display dialog "Test"' ``` ## Limitations - **AppleScript complexity** - Language can be verbose and unintuitive - **Documentation gaps** - Individual application dictionaries vary in quality - **Maintenance burden** - Scripts may break with OS/app updates - **JXA limitations** - Less documentation than AppleScript; some features missing - **Performance** - Slower than compiled languages or direct APIs - **Limited error recovery** - Error messages can be cryptic ## Real-World Examples ### Batch Rename Files ```applescript tell application "Finder" set folderPath to choose folder set fileList to every file of folderPath repeat with thisFile in fileList set oldName to name of thisFile set newName to "renamed_" & oldName set name of thisFile to newName end repeat end tell ``` ### Backup Important Files ```applescript set sourceFolder to (path to home folder as text) & "Documents:" set backupFolder to (path to home folder as text) & "Backups:" tell application "Finder" duplicate every file of folder sourceFolder to folder backupFolder end tell ``` ### Create Time-Based Notes ```applescript set currentDate to current date set dateStr to (month of currentDate as integer) & "-" & (day of currentDate) & "-" & (year of currentDate) tell application "Notes" activate make new note with properties {name:dateStr, body:""} end tell ``` ### Automate Web Browsing ```applescript tell application "Safari" activate open location "https://example.com" delay 2 do JavaScript "window.scrollTo(0, document.body.scrollHeight);" -- Scroll to bottom end tell ``` ### System Monitoring ```bash osascript -e 'do shell script "top -l 1 | grep CPU"' ``` ### Control Presentation Software ```applescript tell application "Keynote" activate present slideshow of document 1 play slideshow end tell ``` ## Installation and Requirements - **Available on** - macOS (Mac OS X 10.5 Leopard or later) - **Location** - `/usr/bin/osascript` - **Shebang** - `#!/usr/bin/osascript` or `#!/usr/bin/env osascript` - **No installation required** - Built into macOS ## Version Information - **AppleScript 2.0+** - Modern AppleScript version - **JXA (JavaScript for Automation)** - Introduced in OS X 10.10 (Yosemite) - **Compatibility** - Most scripts work across multiple macOS versions - **Current support** - Still maintained and available in latest macOS ## Additional Resources ### Official Documentation - Apple AppleScript Language Guide: https://developer.apple.com/library/archive/documentation/AppleScript/Conceptual/AppleScriptLangGuide/ - Mac Automation Scripting Guide: https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/ ### Community Resources - JXA Cookbook: https://github.com/JXA-Cookbook/JXA-Cookbook - MacScripter Forums: https://www.macscripter.net/ - AppleScript User Manual: https://developer.apple.com/library/archive/documentation/AppleScript/ ### Practical Guides - SS64 osascript reference: https://ss64.com/mac/osascript.html - O'Reilly AppleScript: The Definitive Guide (available online) - DEVONtechnologies JXA Guide: https://www.devontechnologies.com/blog/20211005-jxa-javascript-for-applications ## Related Topics - **Apple Events** - Inter-application communication framework - **Script Editor** - IDE for writing and debugging AppleScript - **Automator** - GUI-based automation alternative - **Keyboard Maestro** - Advanced macro automation tool - **FastScripts** - Script management and scheduling - **LaunchAgents** - Scheduled script execution --- # JavaScript for Automation (JXA) # Source: https://github.com/JXA-Cookbook/JXA-Cookbook/wiki/Using-JavaScript-for-Automation # Source: https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/ JavaScript for Automation (JXA) is a JavaScript-based scripting interface for macOS that provides peer functionality to AppleScript. It was introduced in OS X 10.10 (Yosemite) and uses the same automation framework as AppleScript. ## Overview JXA provides: - **Modern JavaScript syntax** - Familiar to web developers - **Apple Event integration** - Access to scriptable macOS applications - **Objective-C bridge** - Direct access to Objective-C frameworks - **Standard library** - Common utilities for file and system operations - **Object-oriented approach** - Work with application objects and properties ## Basic Syntax ### Hello World ```javascript // Simple alert var app = Application.currentApplication() app.activate() app.displayAlert('Hello World') // Console output console.log('Hello from JXA') // Return value (displayed by osascript) 'Done' ``` ### The Run Handler ```javascript // Implicit: the last expression is returned Application("Safari").activate() // Explicit run function (for arguments) function run(argv) { console.log(argv[0]) // First argument return "Finished" } ``` ### Comments ```javascript // Single-line comment console.log('test') /* Multi-line comment */ var x = 1 ``` ## Running JXA Scripts ### With osascript (One-Liner) ```bash osascript -l JavaScript -e 'Application("Safari").activate()' ``` ### From File Create `script.js`: ```javascript Application("Safari").activate() ``` Run with osascript: ```bash osascript -l JavaScript script.js ``` ### Executable Script (Shebang) Create `script.js` with shebang: ```javascript #!/usr/bin/osascript -l JavaScript Application("Safari").activate() ``` Make executable: ```bash chmod +x script.js ./script.js ``` ### Interactive REPL ```bash osascript -i -l JavaScript ``` Then type commands directly: ```javascript > Application("Safari").activate() ``` ## Accessing Applications ### Get Application Reference ```javascript var safari = Application("Safari") var finder = Application("Finder") var system = Application("System Events") ``` ### Activate (Bring to Front) ```javascript Application("Safari").activate() ``` ### Check if Application is Running ```javascript var safari = Application("Safari") if (safari.running()) { console.log("Safari is running") } ``` ### Get Current Application ```javascript var app = Application.currentApplication() ``` ## Working with Application Objects ### Access Application Properties ```javascript var safari = Application("Safari") // Get window information var windows = safari.windows() // Array of windows var activeWindow = safari.windows()[0] // Get tabs (if supported) var tabs = activeWindow.tabs() var currentTab = activeWindow.currentTab() // Get URL var url = currentTab.url() ``` ### Manipulate Application Properties ```javascript var safari = Application("Safari") // Open a new tab safari.open(Path("https://example.com")) // Set URL safari.windows()[0].currentTab().url = "https://example.com" // Get/set properties var finder = Application("Finder") finder.desktop() // Get desktop folder object ``` ### Call Application Methods ```javascript // Open location Application("Safari").openLocation("https://example.com") // Activate window Application("Safari").activate() ``` ## Common Applications ### Safari ```javascript var safari = Application("Safari") // Activate safari.activate() // Open URL safari.openLocation("https://example.com") // Get current tab URL var url = safari.windows()[0].currentTab().url() // Execute JavaScript in page safari.windows()[0].currentTab().doJavaScript("console.log('test')") // Close current tab safari.windows()[0].currentTab().close() ``` ### Finder ```javascript var finder = Application("Finder") // Activate finder.activate() // Get desktop files var desktop = finder.desktop() var files = desktop.files() // Reveal file/folder finder.reveal("/Users/username/Documents") // Get home folder var homeFolder = finder.home() // Make new folder var newFolder = finder.make({ new: "folder", at: homeFolder }) // List folder contents var contents = finder.items({ from: homeFolder }) ``` ### System Events ```javascript var system = Application("System Events") // Get system information var sysInfo = system.systemInfo() // Check dark mode var isDarkMode = system.appearancePreferences.darkMode() // Set dark mode system.appearancePreferences.darkMode = true // Simulate keyboard system.keystroke("a", { using: "command down" }) // Simulate mouse click system.click(point) ``` ### Mail ```javascript var mail = Application("Mail") // Activate mail.activate() // Get accounts var accounts = mail.accounts() // Get mailboxes var mailboxes = mail.mailboxes() // Create message var newMessage = mail.make({ new: "outgoing message" }) newMessage.subject = "Hello" newMessage.content = "Message body" newMessage.send() ``` ### Finder with File Operations ```javascript var finder = Application("Finder") // Get Documents folder var docs = finder.home().folders.byName("Documents")[0] // List files finder.items({ from: docs }).forEach(function(item) { console.log(item.name()) }) // Delete a file var file = docs.files.byName("oldfile.txt")[0] file.delete() // Move file file.moveTo(otherFolder) ``` ## File and Folder Operations ### Working with Paths ```javascript // Import ObjC for path utilities ObjC.import('Foundation') // Create path var path = "/Users/username/Documents" // Home directory var home = $.NSHomeDirectory() // Expand tilde var expanded = $.NSString.stringWithString("~/Documents") .stringByExpandingTildeInPath() // Join paths var joined = $.NSString.stringWithString(home) .stringByAppendingPathComponent("Documents") ``` ### Read File ```javascript // Simple read var file = $.NSString.stringWithContentsOfFile_encoding_error( "/path/to/file", $.NSUTF8StringEncoding, null ) // Or using ObjC ObjC.import('Foundation') var contents = $.NSString.stringWithContentsOfFile_encoding_error( "/path/to/file.txt", $.NSUTF8StringEncoding, null ) console.log(ObjC.unwrap(contents)) ``` ### Write File ```javascript ObjC.import('Foundation') var content = "File content here" var path = "/tmp/test.txt" var string = $.NSString.stringWithString(content) string.writeToFile_atomically_encoding_error( path, true, $.NSUTF8StringEncoding, null ) console.log("File written") ``` ### Check File Existence ```javascript ObjC.import('Foundation') var path = "/path/to/file" var manager = $.NSFileManager.defaultManager if (manager.fileExistsAtPath(path)) { console.log("File exists") } else { console.log("File does not exist") } ``` ### List Files in Directory ```javascript ObjC.import('Foundation') var path = "/Users/username/Documents" var manager = $.NSFileManager.defaultManager var contents = manager.contentsOfDirectoryAtPath_error(path, null) var files = ObjC.unwrap(contents) files.forEach(function(file) { console.log(file) }) ``` ## User Interaction ### Display Alert ```javascript var app = Application.currentApplication() app.activate() app.displayAlert("Alert Title", { message: "This is the message", buttons: ["Cancel", "OK"], defaultButton: 1, cancelButton: 1 }) ``` ### Display Dialog (with Input) ```javascript var app = Application.currentApplication() app.activate() var result = app.displayDialog("Enter your name:", { defaultAnswer: "Default", buttons: ["Cancel", "OK"], defaultButton: "OK", cancelButton: "Cancel" }) var userName = result.textReturned ``` ### Display Notification ```javascript // Simple notification var app = Application.currentApplication() app.activate() app.displayNotification("This is a notification", { withTitle: "Title", subtitle: "Subtitle" }) ``` ### Alert with Buttons ```javascript var app = Application.currentApplication() app.activate() var response = app.displayAlert("Proceed?", { message: "Do you want to continue?", buttons: ["No", "Yes"], defaultButton: 2 }) if (response.buttonReturned === "Yes") { console.log("User clicked Yes") } ``` ## Data Types and Operators ### Basic Types ```javascript // String var str = "Hello" console.log(str) // Number var num = 42 var decimal = 3.14 // Boolean var flag = true // Array var arr = [1, 2, 3, 4, 5] // Object var obj = { name: "John", age: 30 } // Null/Undefined var empty = null var notSet = undefined ``` ### String Operations ```javascript var str = "Hello World" // Length str.length // 11 // Concatenation str + " from JXA" // Substring str.substring(0, 5) // "Hello" // Methods str.toLowerCase() str.toUpperCase() str.includes("World") str.split(" ") // ["Hello", "World"] ``` ### Array Operations ```javascript var arr = [1, 2, 3, 4, 5] // Length arr.length // Access elements arr[0] // 1 // Add element arr.push(6) // Remove element arr.pop() // Iterate arr.forEach(function(item) { console.log(item) }) // Map/Filter arr.map(x => x * 2) arr.filter(x => x > 2) ``` ## Control Flow ### If/Else ```javascript var x = 10 if (x > 5) { console.log("x is greater than 5") } else if (x === 5) { console.log("x is 5") } else { console.log("x is less than 5") } ``` ### Loops ```javascript // For loop for (var i = 0; i < 5; i++) { console.log(i) } // While loop var j = 0 while (j < 5) { console.log(j) j++ } // ForEach [1, 2, 3].forEach(function(item) { console.log(item) }) // For...in (object properties) var obj = { a: 1, b: 2, c: 3 } for (var key in obj) { console.log(key + ": " + obj[key]) } ``` ### Try/Catch ```javascript try { // Code that might error Application("NonExistentApp").activate() } catch (error) { console.log("Error: " + error) } ``` ## Functions ### Define and Call ```javascript function greet(name) { return "Hello, " + name } console.log(greet("World")) ``` ### Arrow Functions ```javascript var add = (a, b) => a + b console.log(add(5, 3)) // 8 var double = x => x * 2 console.log(double(5)) // 10 ``` ### Default Parameters ```javascript function greet(name = "World") { return "Hello, " + name } console.log(greet()) // "Hello, World" ``` ## Objective-C Integration ### Import Frameworks ```javascript ObjC.import('Foundation') ObjC.import('Cocoa') ObjC.import('AppKit') ``` ### Access Objective-C Classes ```javascript ObjC.import('Foundation') // Create object var array = $.NSMutableArray.array() array.addObject_("hello") // Call methods var string = $.NSString.stringWithString("test") console.log(ObjC.unwrap(string)) // Access properties var value = obj.property obj.property = newValue ``` ### Converting Types ```javascript // Unwrap ObjC objects to JavaScript var nsString = $.NSString.stringWithString("hello") var jsString = ObjC.unwrap(nsString) // Wrap JavaScript values to ObjC var nsArray = $(["a", "b", "c"]) ``` ### JSON and Serialization ```javascript ObjC.import('Foundation') var dict = { name: "John", age: 30 } var jsonData = $.NSJSONSerialization.dataWithJSONObject_options_error( dict, 0, null ) var jsonString = $.NSString.alloc.initWithData_encoding(jsonData, $.NSUTF8StringEncoding) console.log(ObjC.unwrap(jsonString)) ``` ## Scripting Bridge Concepts ### Reference Forms ```javascript // By index var file = finder.documents()[0] // By name var file = finder.documents.byName("file.txt")[0] // By ID var file = finder.documents.byId(123)[0] // All items var allFiles = finder.documents() ``` ### Property Access ```javascript var finder = Application("Finder") var desktop = finder.desktop() // Get properties var name = desktop.name() var kind = desktop.kind() // Set properties desktop.name = "New Name" // Properties with parameters var items = finder.items({ from: someFolder }) ``` ## Debugging ### Console Output ```javascript console.log("Log message") console.log("Value:", variable) console.log({ property: value }) ``` ### Inspect Objects ```javascript var app = Application("Safari") console.log(app.windows()) // Show window objects console.log(app.windows()[0].currentTab()) // Show tab object ``` ### Error Messages ```javascript try { someFunction() } catch (error) { console.log("Error name: " + error.name) console.log("Error message: " + error.message) console.log("Full error: " + error) } ``` ### Script Editor Debugging 1. Open `/Applications/Utilities/Script Editor.app` 2. Change language to "JavaScript" 3. Paste JXA code 4. Click "Run" 5. View results in output panel 6. Errors show in console ## Common Patterns ### Process Finder Files ```javascript var finder = Application("Finder") var desktop = finder.desktop() var files = desktop.files() files.forEach(function(file) { var name = file.name() var kind = file.kind() console.log(name + ": " + kind) }) ``` ### Batch Rename Files ```javascript var finder = Application("Finder") var docs = finder.documents() // Or any folder docs.forEach(function(file, index) { var oldName = file.name() var newName = "file_" + (index + 1) file.name = newName console.log(oldName + " -> " + newName) }) ``` ### Create Folder Structure ```javascript var finder = Application("Finder") var desktop = finder.desktop() var mainFolder = finder.make({ new: "folder", at: desktop, withProperties: { name: "MyProject" } }) var subFolder = finder.make({ new: "folder", at: mainFolder, withProperties: { name: "SubFolder" } }) console.log("Folders created") ``` ### Check System Properties ```javascript var system = Application("System Events") // Dark mode var isDark = system.appearancePreferences.darkMode() console.log("Dark mode: " + isDark) // Get computer name var computerName = system.computerName() console.log("Computer: " + computerName) // Get user name var userName = system.userName() console.log("User: " + userName) ``` ### Open Multiple Applications ```javascript var apps = ["Safari", "Mail", "Finder"] apps.forEach(function(appName) { Application(appName).activate() delay(0.5) // Wait 0.5 seconds }) ``` ## Limitations - **No multi-threading** - Single-threaded execution - **Limited documentation** - Less comprehensive than AppleScript - **Performance** - Can be slower than compiled languages - **Compatibility** - Requires macOS 10.10+ - **Sandboxing** - Restricted for sandboxed apps - **Error recovery** - Cryptic error messages sometimes ## Performance Tips 1. **Minimize application activation** - Bringing apps to front is slow 2. **Batch operations** - Do multiple operations per app access 3. **Cache references** - Store application objects instead of recreating 4. **Use background scripts** - Don't rely on UI automation when possible 5. **Avoid redundant operations** - Check before repeating actions ## Resources - **JXA Cookbook**: https://github.com/JXA-Cookbook/JXA-Cookbook - **Apple Automation Guide**: https://developer.apple.com/library/archive/documentation/LanguagesUtilities/Conceptual/MacAutomationScriptingGuide/ - **Objective-C Bridge**: Bridge between JXA and native Objective-C frameworks - **Script Editor**: Built-in IDE for writing and testing JXA scripts ## Tips for Learning 1. **Start with Script Editor** - Test syntax interactively 2. **Check application dictionary** - Open/Show Library in Script Editor 3. **Use console.log** - Debug output like JavaScript in browser 4. **Try existing examples** - JXA Cookbook has many working examples 5. **Convert AppleScript** - Most AppleScript can be converted to JXA