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