{"id":22325523,"url":"https://github.com/jamf/subprocess","last_synced_at":"2025-10-20T11:55:15.610Z","repository":{"id":49002102,"uuid":"242790101","full_name":"jamf/Subprocess","owner":"jamf","description":"Swift library for macOS providing interfaces for both synchronous and asynchronous process execution","archived":false,"fork":false,"pushed_at":"2025-03-19T17:56:20.000Z","size":208,"stargazers_count":37,"open_issues_count":3,"forks_count":10,"subscribers_count":18,"default_branch":"main","last_synced_at":"2025-08-09T01:52:12.767Z","etag":null,"topics":["carthage","cocoapods","macos","swift","swift-package-manager"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jamf.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":"CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-02-24T16:48:06.000Z","updated_at":"2025-06-18T19:11:39.000Z","dependencies_parsed_at":"2025-07-29T16:33:30.389Z","dependency_job_id":"34ce3a5b-5c6c-447a-a6df-ca70e6d09f72","html_url":"https://github.com/jamf/Subprocess","commit_stats":null,"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"purl":"pkg:github/jamf/Subprocess","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamf%2FSubprocess","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamf%2FSubprocess/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamf%2FSubprocess/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamf%2FSubprocess/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jamf","download_url":"https://codeload.github.com/jamf/Subprocess/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jamf%2FSubprocess/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":274418192,"owners_count":25281266,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-10T02:00:12.551Z","response_time":83,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["carthage","cocoapods","macos","swift","swift-package-manager"],"created_at":"2024-12-04T02:12:25.053Z","updated_at":"2025-10-20T11:55:15.536Z","avatar_url":"https://github.com/jamf.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Subprocess\n[![License](http://img.shields.io/badge/license-MIT-informational.svg?style=flat)](http://mit-license.org)\n![Build](https://github.com/jamf/Subprocess/workflows/Build%20\u0026%20Test/badge.svg)\n[![CocoaPods](https://img.shields.io/cocoapods/v/Subprocess.svg)](https://cocoapods.org/pods/Subprocess)\n[![Platform](https://img.shields.io/badge/platform-macOS-success.svg?style=flat)](https://developer.apple.com/macos)\n[![Language](http://img.shields.io/badge/language-Swift-success.svg?style=flat)](https://developer.apple.com/swift)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![SwiftPM compatible](https://img.shields.io/badge/spm-compatible-success.svg?style=flat)](https://swift.org/package-manager)\n[![Documentation](https://img.shields.io/badge/documentation-100%25-green)](https://engineering.jamf.com/Subprocess/documentation/subprocess/)\n\nSubprocess is a Swift library for macOS providing interfaces for both synchronous and asynchronous process execution. \nSubprocessMocks can be used in unit tests for quick and highly customizable mocking and verification of Subprocess usage. \n\n- [Usage](#usage)\n    - [Subprocess Class](#subprocess-class)\n        - [Command Input](#command-input) - [Data](#input-for-data), [Text](#input-for-text), [File](#input-for-file-url)\n        - [Command Output](#command-output) - [Data](#output-as-data), [Text](#output-as-string), [Decodable JSON object](#output-as-decodable-object-from-json), [Decodable property list object](#output-as-decodable-object-from-property-list)\n- [Installation](#installation)\n    - [SwiftPM](#swiftpm)\n    - [Cocoapods](#cocoapods)\n    - [Carthage](#carthage)\n\n[Full Documentation](./docs/index.html)\n\n# Usage\n### Subprocess Class\nThe `Subprocess` class can be used for command execution.\n\n#### Command Input\n\n###### Input for data\n```swift\nlet inputData = Data(\"hello world\".utf8)\nlet data = try await Subprocess.data(for: [\"/usr/bin/grep\", \"hello\"], standardInput: inputData)\n```\n###### Input for text\n```swift\nlet data = try await Subprocess.data(for: [\"/usr/bin/grep\", \"hello\"], standardInput: \"hello world\")\n```\n###### Input for file URL\n```swift\nlet data = try await Subprocess.data(for: [\"/usr/bin/grep\", \"foo\"], standardInput: URL(filePath: \"/path/to/input/file\"))\n```\n\n#### Command Output\n\n###### Output as Data\n```swift\nlet data = try await Subprocess.data(for: [\"/usr/bin/sw_vers\"])\n```\n###### Output as String\n```swift\nlet string = try await Subprocess.string(for: [\"/usr/bin/sw_vers\"])\n```\n###### Output as decodable object from JSON\n```swift\nstruct LogMessage: Codable {\n    var subsystem: String\n    var category: String\n    var machTimestamp: UInt64\n}\n\nlet result: [LogMessage] = try await Subprocess.value(for: [\"/usr/bin/log\", \"show\", \"--style\", \"json\", \"--last\", \"30s\"], decoder: JSONDecoder())\n```\n###### Output as decodable object from Property List\n```swift\nstruct SystemVersion: Codable {\n    enum CodingKeys: String, CodingKey {\n        case version = \"ProductVersion\"\n    }\n    var version: String\n}\n\nlet result: SystemVersion = try await Subprocess.value(for: [\"/bin/cat\", \"/System/Library/CoreServices/SystemVersion.plist\"], decoder: PropertyListDecoder())\n```\n###### Output mapped to other type \n```swift\nlet enabled = try await Subprocess([\"/usr/bin/csrutil\", \"status\"]).run().standardOutput.lines.first(where: { $0.contains(\"enabled\") } ) != nil\n```\n###### Output options\n```swift\nlet errorText = try await Subprocess.string(for: [\"/usr/bin/cat\", \"/non/existent/file.txt\"], options: .returnStandardError)\nlet outputText = try await Subprocess.string(for: [\"/usr/bin/sw_vers\"])\n\nasync let (standardOutput, standardError, _) = try Subprocess([\"/usr/bin/csrutil\", \"status\"]).run()\nlet combinedOutput = try await [standardOutput.string(), standardError.string()]\n```\n###### Handling output as it is read\n```swift\nlet (stream, input) = {\n    var input: AsyncStream\u003cUInt8\u003e.Continuation!\n    let stream: AsyncStream\u003cUInt8\u003e = AsyncStream { continuation in\n        input = continuation\n    }\n\n    return (stream, input!)\n}()\n\nlet subprocess = Subprocess([\"/bin/cat\"])\nlet (standardOutput, _, waitForExit) = try subprocess.run(standardInput: stream)\n\ninput.yield(\"hello\\n\")\n\nTask {\n    for await line in standardOutput.lines {\n        switch line {\n        case \"hello\":\n            input.yield(\"world\\n\")\n        case \"world\":\n            input.yield(\"and\\nuniverse\")\n            input.finish()\n        case \"universe\":\n            await waitForExit()\n            break\n        default:\n            continue\n        }\n    }\n}\n```\n###### Handling output on termination\n```swift\nlet process = Subprocess([\"/usr/bin/csrutil\", \"status\"])\nlet (standardOutput, standardError, waitForExit) = try process.run()\nasync let (stdout, stderr) = (standardOutput, standardError)\nlet combinedOutput = await [stdout.data(), stderr.data()]\n\nawait waitForExit()\n\nif process.exitCode == 0 {\n    // Do something with output data\n} else {\n    // Handle failure\n}\n```\n###### Closure based callbacks\n```swift\nlet command: [String] = ...\nlet process = Subprocess(command)\nnonisolated(unsafe) var outputData: Data?\nnonisolated(unsafe) var errorData: Data?\n\n// The outputHandler and errorHandler are invoked serially\ntry process.launch(outputHandler: { data in\n    // Handle new data read from stdout\n    outputData = data \n}, errorHandler: { data in\n    // Handle new data read from stderr\n    errorData = data\n}, terminationHandler: { process in\n    // Handle process termination, all scheduled calls to\n    // the outputHandler and errorHandler are guaranteed to\n    // have completed.\n})\n```\n###### Handing output on termination with a closure\n```swift\nlet command: [String] = ...\nlet process = Subprocess(command)\n\ntry process.launch { (process, outputData, errorData) in\n    if process.exitCode == 0 {\n        // Do something with output data\n    } else {\n        // Handle failure\n    }\n```\n\n## Installation\n### SwiftPM\n```swift\nlet package = Package(\n    // name, platforms, products, etc.\n    dependencies: [\n        // other dependencies\n        .package(url: \"https://github.com/jamf/Subprocess.git\", .upToNextMajor(from: \"3.0.0\")),\n    ],\n    targets: [\n        .target(name: \"\u003ctarget\u003e\",\n        dependencies: [\n            // other dependencies\n            .product(name: \"Subprocess\"),\n        ]),\n        // other targets\n    ]\n)\n```\n### Cocoapods\n```ruby\npod 'Subprocess'\n```\n### Carthage\n```ruby\ngithub 'jamf/Subprocess'\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamf%2Fsubprocess","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjamf%2Fsubprocess","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjamf%2Fsubprocess/lists"}