Hi Nick,

Thanks for this survey of other languages, it’s very useful.

I think if we were to add something, I would prefer to keep it simple. Just one 
class method on Process which is a fairly straightforward wrapper of the other 
functionality already there.

I was thinking of perhaps a completion handler version, with the expectation 
that once async/await style completions land it would be pretty easy to use in 
a kind of straight-line mechanism.

- Tony

> On Nov 20, 2017, at 4:37 AM, Nick Keets <nick.ke...@gmail.com> wrote:
> 
> Looking at what Python (subprocess) and Go (os.exec) do, it looks like they 
> agree on the following:
>  - executable and arguments are merged in one array
>  - they don't require full path for the executable
>  - they don't expand tildes
>  - blocking calls are the default
>  - they are more explicit about stdin, stdout, stderr
> 
> Some example scenarios based on that, with possible swift code:
> 
> 1) Run a command and ignore output
> 
> Python:
>     subprocess.run(["sleep", "1"], stdout=subprocess.DEVNULL, 
> stderr=subprocess.DEVNULL)
> 
> Go:
>     cmd := exec.Command("sleep", "1")
>     err := cmd.Run()
> 
> Possible Swift:
>     try Process.run(["sleep", "1"])
> 
> 
> 2) Run a command and capture stdout
> 
> Python:
>     out = subprocess.check_output(["ls", "-l"])
> 
> Go:
> 
>     cmd := exec.Command("ls", "-l")
>     out, err := cmd.Output()
> 
> Possible Swift:
> 
>     let proc = try Process.run(["ls", "-l"])
>     let out = proc.stdout.read() // proc.stdout is OutputStream, assumes 
> read() exists and returns Data
>     // stderr available at proc.stderr
> 
> 
> 3) Run a command and capture both stdout and stder together
> 
> Python:
>     out = subprocess.check_output(["ls", "-l"], stderr=subprocess.STDOUT)
> 
> Go:
>     cmd := exec.Command("ls", "-l")
>     out, err := cmd.CombinedOutput()
> 
> Possible Swift:
>     let proc = try Process.run(["ls", "-l"], combinedOutput: true)
>     let out = proc.stdout.read()
> 
> 
> 4) Shell out
> 
> Python:
>     subprocess.check_output(["ls", "-l"], stderr=subprocess.STDOUT, 
> shell=True)
> 
> Go:
>     cmd := exec.Command("sh", "-c", "ls -l")
>     out, err := cmd.CombinedOutput()
> 
> Possible Swift:
>     let proc = try Process.run(["sh", "-c", "ls -l"], combinedOutput: true)
>     let out = proc.stdout.read()
> 
> 
> 5) Pipe to stdin
> 
> Python:
>     p = subprocess.Popen(["wc"], stdin=subprocess.PIPE, 
> stdout=subprocess.PIPE)
>     p.stdin.write(b'blah')
>     p.stdin.close()
>     out = p.stdout.read()
> 
> Go:
>     cmd := exec.Command("wc")
>     stdin, err := cmd.StdinPipe()
>     io.WriteString(stdin, "blah")
>     out = cmd.CombinedOutput()
> 
> Possible Swift:
>     let stdin = InputStream(data: "blah".data(using: .utf8))
>     let proc = try Process.run(["wc"], stdin: stdin, combinedOutput: true)
>     let out = proc.stdout.read()
> 
> 6) Async
> 
> Python:
>     p = subprocess.Popen(["sleep", "5"])
>     p.wait()
> 
> Go:
>     cmd := exec.Command("sleep", "5")
>     err := cmd.Start()
>     err2 := cmd.Wait()
> 
> Possible Swift:
>     let proc = Process(["sleep", "5"])
>     try proc.start()
>     try proc.wait()
> 
> 
> On Fri, Nov 17, 2017 at 9:34 PM, Tony Parker via swift-corelibs-dev 
> <swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>> wrote:
> Hi Abhi,
> 
> It does seem like there is a possibility of some better convenience API here.
> 
> Any ideas on what form it would take? A class method on Process that returns 
> the output, maybe?
> 
> - Tony
> 
>> On Nov 16, 2017, at 3:34 PM, Abhi Beckert via swift-corelibs-dev 
>> <swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>> wrote:
>> 
>> Swift is a great shell scripting language except for it's lack of any API to 
>> execute UNIX commands. Compare these two shell scripts:
>> 
>>> #!/usr/bin/php
>>> <?
>>> 
>>> $files = `find ~/Desktop -name *.png`;
>>> 
>>> foreach (explode("\n", $files) as $file) {
>>>   // do something with $file
>>> }
>> 
>> -
>> 
>>> #!/usr/bin/swift
>>> 
>>> import Foundation
>>> 
>>> let process = Process()
>>> process.launchPath = "/usr/bin/find"
>>> process.arguments = [
>>>   NSString(string:"~/Desktop").expandingTildeInPath,
>>>   "-name",
>>>   "*.png"
>>> ]
>>> 
>>> let output = Pipe()
>>> process.standardOutput = output
>>> 
>>> process.launch()
>>> 
>>> let files: String
>>> if let filesUtf8 = NSString(data: 
>>> output.fileHandleForReading.readDataToEndOfFile(), encoding: 
>>> String.Encoding.utf8.rawValue) {
>>>   files = filesUtf8 as String
>>> } else {
>>>   files = NSString(data: output.fileHandleForReading.readDataToEndOfFile(), 
>>> encoding: String.Encoding.isoLatin1.rawValue) as NSString! as String
>>> }
>>> 
>>> files.enumerateLines { file, _ in
>>>   // do something with file
>>> }
>> 
>> It's a contrived example, I could have used NSFileManager, but I run into 
>> this all the time integrating with more complex tools such as rsync.
>> 
>> Adding my own high level wrapper around the Process command isn't an option 
>> since there is no good way to import code from another file when executing 
>> swift asa shell script. All your code needs to be in one file.
>> 
>> - Abhi
>> _______________________________________________
>> swift-corelibs-dev mailing list
>> swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>
>> https://lists.swift.org/mailman/listinfo/swift-corelibs-dev 
>> <https://lists.swift.org/mailman/listinfo/swift-corelibs-dev>
> 
> _______________________________________________
> swift-corelibs-dev mailing list
> swift-corelibs-dev@swift.org <mailto:swift-corelibs-dev@swift.org>
> https://lists.swift.org/mailman/listinfo/swift-corelibs-dev 
> <https://lists.swift.org/mailman/listinfo/swift-corelibs-dev>
> 
> 

_______________________________________________
swift-corelibs-dev mailing list
swift-corelibs-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-corelibs-dev

Reply via email to