<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Hi Nick,<div class=""><br class=""></div><div class="">Thanks for this survey of other languages, it’s very useful.</div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">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.</div><div class=""><br class=""></div><div class="">- Tony<br class=""><div><br class=""><blockquote type="cite" class=""><div class="">On Nov 20, 2017, at 4:37 AM, Nick Keets &lt;<a href="mailto:nick.keets@gmail.com" class="">nick.keets@gmail.com</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div dir="ltr" class=""><div class="">Looking at what Python (subprocess) and Go (os.exec) do, it looks like they agree on the following:</div><div class="">&nbsp;- executable and arguments are merged in one array</div><div class="">&nbsp;- they don't require full path for the executable</div><div class="">&nbsp;- they don't expand tildes</div><div class="">&nbsp;- blocking calls are the default</div><div class="">&nbsp;- they are more explicit about stdin, stdout, stderr</div><div class=""><br class=""></div><div class="">Some example scenarios based on that, with possible swift code:</div><div class=""><br class=""></div><div class="">1) Run a command and ignore output</div><div class=""><br class=""></div><div class="">Python:</div><div class="">&nbsp; &nbsp; subprocess.run(["sleep", "1"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)</div><div class=""><br class=""></div><div class="">Go:</div><div class="">&nbsp; &nbsp; cmd := exec.Command("sleep", "1")</div><div class="">&nbsp; &nbsp; err := cmd.Run()</div><div class=""><br class=""></div><div class="">Possible Swift:</div><div class="">&nbsp; &nbsp; try Process.run(["sleep", "1"])</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">2) Run a command and capture stdout</div><div class=""><br class=""></div><div class="">Python:</div><div class="">&nbsp; &nbsp; out = subprocess.check_output(["ls", "-l"])</div><div class=""><br class=""></div><div class="">Go:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; cmd := exec.Command("ls", "-l")</div><div class="">&nbsp; &nbsp; out, err := cmd.Output()</div><div class=""><br class=""></div><div class="">Possible Swift:</div><div class=""><br class=""></div><div class="">&nbsp; &nbsp; let proc = try Process.run(["ls", "-l"])</div><div class="">&nbsp; &nbsp; let out = proc.stdout.read() // proc.stdout is OutputStream, assumes read() exists and returns Data</div><div class="">&nbsp; &nbsp; // stderr available at proc.stderr</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">3) Run a command and capture both stdout and stder together</div><div class=""><br class=""></div><div class="">Python:</div><div class="">&nbsp; &nbsp; out = subprocess.check_output(["ls", "-l"], stderr=subprocess.STDOUT)</div><div class=""><br class=""></div><div class="">Go:</div><div class="">&nbsp; &nbsp; cmd := exec.Command("ls", "-l")</div><div class="">&nbsp; &nbsp; out, err := cmd.CombinedOutput()</div><div class=""><br class=""></div><div class="">Possible Swift:</div><div class="">&nbsp; &nbsp; let proc = try Process.run(["ls", "-l"], combinedOutput: true)</div><div class="">&nbsp; &nbsp; let out = proc.stdout.read()</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">4) Shell out</div><div class=""><br class=""></div><div class="">Python:</div><div class="">&nbsp; &nbsp; subprocess.check_output(["ls", "-l"], stderr=subprocess.STDOUT, shell=True)</div><div class=""><br class=""></div><div class="">Go:</div><div class="">&nbsp; &nbsp; cmd := exec.Command("sh", "-c", "ls -l")</div><div class="">&nbsp; &nbsp; out, err := cmd.CombinedOutput()</div><div class=""><br class=""></div><div class="">Possible Swift:</div><div class="">&nbsp; &nbsp; let proc = try Process.run(["sh", "-c", "ls -l"], combinedOutput: true)</div><div class="">&nbsp; &nbsp; let out = proc.stdout.read()</div><div class=""><br class=""></div><div class=""><br class=""></div><div class="">5) Pipe to stdin</div><div class=""><br class=""></div><div class="">Python:</div><div class="">&nbsp; &nbsp; p = subprocess.Popen(["wc"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)</div><div class="">&nbsp; &nbsp; p.stdin.write(b'blah')</div><div class="">&nbsp; &nbsp; p.stdin.close()</div><div class="">&nbsp; &nbsp; out = p.stdout.read()</div><div class=""><br class=""></div><div class="">Go:</div><div class="">&nbsp; &nbsp; cmd := exec.Command("wc")</div><div class="">&nbsp; &nbsp; stdin, err := cmd.StdinPipe()</div><div class="">&nbsp; &nbsp; io.WriteString(stdin, "blah")</div><div class="">&nbsp; &nbsp; out = cmd.CombinedOutput()</div><div class=""><br class=""></div><div class="">Possible Swift:</div><div class="">&nbsp; &nbsp; let stdin = InputStream(data: "blah".data(using: .utf8))</div><div class="">&nbsp; &nbsp; let proc = try Process.run(["wc"], stdin: stdin, combinedOutput: true)</div><div class="">&nbsp; &nbsp; let out = proc.stdout.read()</div><div class=""><br class=""></div><div class="">6) Async</div><div class=""><br class=""></div><div class="">Python:</div><div class="">&nbsp; &nbsp; p = subprocess.Popen(["sleep", "5"])</div><div class="">&nbsp; &nbsp; p.wait()</div><div class=""><br class=""></div><div class="">Go:</div><div class="">&nbsp; &nbsp; cmd := exec.Command("sleep", "5")</div><div class="">&nbsp; &nbsp; err := cmd.Start()</div><div class="">&nbsp; &nbsp; err2 := cmd.Wait()</div><div class=""><br class=""></div><div class="">Possible Swift:</div><div class="">&nbsp; &nbsp; let proc = Process(["sleep", "5"])</div><div class="">&nbsp; &nbsp; try proc.start()</div><div class="">&nbsp; &nbsp; try proc.wait()</div><div class=""><br class=""></div></div><div class="gmail_extra"><br class=""><div class="gmail_quote">On Fri, Nov 17, 2017 at 9:34 PM, Tony Parker via swift-corelibs-dev <span dir="ltr" class="">&lt;<a href="mailto:swift-corelibs-dev@swift.org" target="_blank" class="">swift-corelibs-dev@swift.org</a>&gt;</span> wrote:<br class=""><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div style="word-wrap:break-word;line-break:after-white-space" class="">Hi Abhi,<div class=""><br class=""></div><div class="">It does seem like there is a possibility of some better convenience API here.</div><div class=""><br class=""></div><div class="">Any ideas on what form it would take? A class method on Process that returns the output, maybe?</div><div class=""><br class=""></div><div class="">- Tony<br class=""><div class=""><br class=""><blockquote type="cite" class=""><div class=""><div class="h5"><div class="">On Nov 16, 2017, at 3:34 PM, Abhi Beckert via swift-corelibs-dev &lt;<a href="mailto:swift-corelibs-dev@swift.org" target="_blank" class="">swift-corelibs-dev@swift.org</a>&gt; wrote:</div><br class="m_-3069825369200109882Apple-interchange-newline"></div></div><div class=""><div class=""><div class="h5"><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class="">Swift is a great shell scripting language except for it's lack of any API to execute UNIX commands. Compare these two shell scripts:<br class=""></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><br class=""></div><blockquote type="cite" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><div class="">#!/usr/bin/php<br class=""></div><div class="">&lt;?<br class=""></div><div class=""><br class=""></div><div class="">$files = `find ~/Desktop -name *.png`;<br class=""></div><div class=""><br class=""></div><div class="">foreach (explode("\n", $files) as $file) {<br class=""></div><div class="">&nbsp; // do something with $file<br class=""></div><div class="">}<br class=""></div></blockquote><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><br class=""></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class="">-<br class=""></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><br class=""></div><blockquote type="cite" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><div class="">#!/usr/bin/swift<br class=""></div><div class=""><br class=""></div><div class="">import Foundation<br class=""></div><div class=""><br class=""></div><div class="">let process = Process()<br class=""></div><div class="">process.launchPath = "/usr/bin/find"<br class=""></div><div class="">process.arguments = [<br class=""></div><div class="">&nbsp; NSString(string:"~/Desktop").<wbr class="">expandingTildeInPath,<br class=""></div><div class="">&nbsp; "-name",<br class=""></div><div class="">&nbsp; "*.png"<br class=""></div><div class="">]<br class=""></div><div class=""><br class=""></div><div class="">let output = Pipe()<br class=""></div><div class="">process.standardOutput = output<br class=""></div><div class=""><br class=""></div><div class="">process.launch()<br class=""></div><div class=""><br class=""></div><div class="">let files: String<br class=""></div><div class="">if let filesUtf8 = NSString(data: output.fileHandleForReading.<wbr class="">readDataToEndOfFile(), encoding: String.Encoding.utf8.rawValue) {<br class=""></div><div class="">&nbsp; files = filesUtf8 as String<br class=""></div><div class="">} else {<br class=""></div><div class="">&nbsp; files = NSString(data: output.fileHandleForReading.<wbr class="">readDataToEndOfFile(), encoding: String.Encoding.isoLatin1.<wbr class="">rawValue) as NSString! as String<br class=""></div><div class="">}<br class=""></div><div class=""><br class=""></div><div class="">files.enumerateLines { file, _ in<br class=""></div><div class="">&nbsp; // do something with file<br class=""></div><div class="">}<br class=""></div></blockquote><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><br class=""></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class="">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.<br class=""></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><br class=""></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class="">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.<br class=""></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><br class=""></div><div style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class="">- Abhi</div></div></div><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important" class="">______________________________<wbr class="">_________________</span><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><span style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px;float:none;display:inline!important" class="">swift-corelibs-dev mailing list</span><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><a href="mailto:swift-corelibs-dev@swift.org" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" target="_blank" class="">swift-corelibs-dev@swift.org</a><br style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" class=""><a href="https://lists.swift.org/mailman/listinfo/swift-corelibs-dev" style="font-family:Helvetica;font-size:12px;font-style:normal;font-variant-caps:normal;font-weight:normal;letter-spacing:normal;text-align:start;text-indent:0px;text-transform:none;white-space:normal;word-spacing:0px" target="_blank" class="">https://lists.swift.org/<wbr class="">mailman/listinfo/swift-<wbr class="">corelibs-dev</a></div></blockquote></div><br class=""></div></div><br class="">______________________________<wbr class="">_________________<br class="">
swift-corelibs-dev mailing list<br class="">
<a href="mailto:swift-corelibs-dev@swift.org" class="">swift-corelibs-dev@swift.org</a><br class="">
<a href="https://lists.swift.org/mailman/listinfo/swift-corelibs-dev" rel="noreferrer" target="_blank" class="">https://lists.swift.org/<wbr class="">mailman/listinfo/swift-<wbr class="">corelibs-dev</a><br class="">
<br class=""></blockquote></div><br class=""></div>
</div></blockquote></div><br class=""></div></body></html>