- Notifications
You must be signed in to change notification settings - Fork26
Subprocess is a cross-platform package for spawning processes in Swift.
License
swiftlang/swift-subprocess
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
Subprocess is a cross-platform package for spawning processes in Swift.
To useSubprocess
in aSwiftPM project, add it as a package dependency to yourPackage.swift
:
dependencies:[.package(url:"https://github.com/swiftlang/swift-subprocess.git", branch:"main")]
Then, adding theSubprocess
module to your target dependencies:
.target( name:"MyTarget", dependencies:[.product(name:"Subprocess",package:"swift-subprocess")])
Subprocess
offers twopackage traits:
SubprocessFoundation
: includes a dependency onFoundation
and adds extensions on Foundation types likeData
. This trait is enabled by default.SubprocessSpan
: makes Subprocess’ API, mainlyOutputProtocol
,RawSpan
based. This trait is enabled wheneverRawSpan
is available and should only be disabled whenRawSpan
is not available.
Please find the API proposalhere.
The minimal supported Swift version isSwift 6.1.
To experiment with theSubprocessSpan
trait, Swift 6.2 is required. Currently, you can download the Swift 6.2 toolchain (main
development snapshot)here.
The easiest way to spawn a process withSubprocess
is to simply run it and await itsCollectedResult
:
import Subprocessletresult=tryawaitrun(.name("ls"))print(result.processIdentifier) // prints 1234print(result.terminationStatus) // prints exited(0)print(result.standardOutput) // prints LICENSE Package.swift ...
To have more precise control over input and output, you can provide a custom closure that executes while the child process is active. Inside this closure, you have the ability to manage the subprocess’s state (like suspending or terminating it) and stream its standard output and standard error as anAsyncSequence
:
import Subprocess// Monitor Nginx log via `tail -f`asyncletmonitorResult=run(.path("/usr/bin/tail"), arguments:["-f","/path/to/nginx.log"]){ execution, standardOutputinfortryawaitlinein standardOutput.lines(encoding:UTF8.self){ // Parse the log textif line.contains("500"){ // Oh no, 500 error}}}
WhileSubprocess
is designed with Swift’s structural concurrency in mind, it also provides a lower level, synchronous method for launching child processes. However, sinceSubprocess
can’t synchronously monitor child process’s state or handle cleanup, you’ll need to attach a FileDescriptor to each I/O directly. Remember to close theFileDescriptor
once you’re finished.
import Subprocessletinput:FileDescriptor=...input.closeAfter{letpid=tryrunDetached(.path("/bin/daemon"), input: input) // ... other opeartions}
You can set various parameters when running the child process, such asArguments
,Environment
, and working directory:
import Subprocessletresult=tryawaitrun(.path("/bin/ls"), arguments:["-a"], // Inherit the environment values from parent process and // add `NewKey=NewValue` environment:.inherit.updating(["NewKey":"NewValue"]), workingDirectory:"/Users/",)
Subprocess
providesplatform-specific configuration options, like settinguid
andgid
on Unix and adjusting window style on Windows, through thePlatformOptions
struct. Check out thePlatformOptions
documentation for a complete list of configurable parameters across different platforms.
PlatformOptions
also allows access to platform-specific spawning constructs and customizations via a closure.
import Darwinimport SubprocessvarplatformOptions=PlatformOptions()letintendedWorkingDir="/path/to/directory"platformOptions.preSpawnProcessConfigurator={ spawnAttr, fileAttrin // Set POSIX_SPAWN_SETSID flag, which implies calls // to setsidvarflags:Int16=0posix_spawnattr_getflags(&spawnAttr,&flags)posix_spawnattr_setflags(&spawnAttr, flags |Int16(POSIX_SPAWN_SETSID)) // Change the working directory intendedWorkingDir.withCString{ pathin _=posix_spawn_file_actions_addchdir_np(&fileAttr, path)}}letresult=tryawaitrun(.path("/bin/exe"), platformOptions: platformOptions)
By default,Subprocess
:
- Doesn’t send any input to the child process’s standard input
- Captures the child process’s standard output as a
String
, up to 128kB - Ignores the child process’s standard error
You can tailor howSubprocess
handles the standard input, standard output, and standard error by setting theinput
,output
, anderror
parameters:
letcontent="Hello Subprocess"// Send "Hello Subprocess" to the standard input of `cat`letresult=tryawaitrun(.name("cat"), input:.string(content, using:UTF8.self))// Collect both standard error and standard output as Dataletresult=tryawaitrun(.name("cat"), output:.data, error:.data)
Subprocess
supports these input options:
This option means no input is sent to the subprocess.
Use it by setting.none
forinput
.
This option reads input from a specifiedFileDescriptor
. IfcloseAfterSpawningProcess
is set totrue
, the subprocess will close the file descriptor after spawning. Iffalse
, you are responsible for closing it, even if the subprocess fails to spawn.
Use it by setting.fileDescriptor(closeAfterSpawningProcess:)
forinput
.
This option reads input from a type conforming toStringProtocol
using the specified encoding.
Use it by setting.string(using:)
forinput
.
This option reads input from an array ofUInt8
.
Use it by setting.array
forinput
.
This option reads input from a givenData
.
Use it by setting.data
forinput
.
This option reads input from a sequence ofData
.
Use it by setting.sequence
forinput
.
This option reads input from an async sequence ofData
.
Use it by setting.asyncSequence
forinput
.
Subprocess
also supports these output options:
This option means theSubprocess
won’t collect or redirect output from the child process.
Use it by setting.discarded
foroutput
orerror
.
This option writes output to a specifiedFileDescriptor
. You can choose to have theSubprocess
close the file descriptor after spawning.
Use it by setting.fileDescriptor(closeAfterSpawningProcess:)
foroutput
orerror
.
This option collects output as aString
with the given encoding.
Use it by setting.string
or.string(limit:encoding:)
foroutput
orerror
.
This option collects output as[UInt8]
.
Use it by setting.bytes
or.bytes(limit:)
foroutput
orerror
.
Subprocess
works on macOS, Linux, and Windows, with feature parity on all platforms as well as platform-specific options for each.
The table below describes the current level of support that Subprocess has for various platforms:
Platform | Support Status |
---|---|
macOS | Supported |
iOS | Not supported |
watchOS | Not supported |
tvOS | Not supported |
visionOS | Not supported |
Ubuntu 22.04 | Supported |
Windows | Supported |
The latest API documentation can be viewed by running the following command:
swift package --disable-sandbox preview-documentation --target Subprocess
Subprocess is part of the Foundation project. Discussion and evolution take place onSwift Foundation Forum.
If you find something that looks like a bug, please open aBug Report! Fill out as many details as you can.
Like all Swift.org projects, we would like the Subprocess project to foster a diverse and friendly community. We expect contributors to adhere to theSwift.org Code of Conduct.
The Foundation Workgroup communicates with the broader Swift community using theforum for general discussions.
The workgroup can also be contacted privately by messaging@foundation-workgroup on the Swift Forums.
About
Subprocess is a cross-platform package for spawning processes in Swift.
Resources
License
Code of conduct
Security policy
Uh oh!
There was an error while loading.Please reload this page.
Stars
Watchers
Forks
Packages0
Uh oh!
There was an error while loading.Please reload this page.