<div dir="ltr"><div>Looking at what Python (subprocess) and Go (os.exec) do, it looks like they agree on the following:</div><div> - executable and arguments are merged in one array</div><div> - they don't require full path for the executable</div><div> - they don't expand tildes</div><div> - blocking calls are the default</div><div> - they are more explicit about stdin, stdout, stderr</div><div><br></div><div>Some example scenarios based on that, with possible swift code:</div><div><br></div><div>1) Run a command and ignore output</div><div><br></div><div>Python:</div><div> subprocess.run(["sleep", "1"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)</div><div><br></div><div>Go:</div><div> cmd := exec.Command("sleep", "1")</div><div> err := cmd.Run()</div><div><br></div><div>Possible Swift:</div><div> try Process.run(["sleep", "1"])</div><div><br></div><div><br></div><div>2) Run a command and capture stdout</div><div><br></div><div>Python:</div><div> out = subprocess.check_output(["ls", "-l"])</div><div><br></div><div>Go:</div><div><br></div><div> cmd := exec.Command("ls", "-l")</div><div> out, err := cmd.Output()</div><div><br></div><div>Possible Swift:</div><div><br></div><div> let proc = try Process.run(["ls", "-l"])</div><div> let out = proc.stdout.read() // proc.stdout is OutputStream, assumes read() exists and returns Data</div><div> // stderr available at proc.stderr</div><div><br></div><div><br></div><div>3) Run a command and capture both stdout and stder together</div><div><br></div><div>Python:</div><div> out = subprocess.check_output(["ls", "-l"], stderr=subprocess.STDOUT)</div><div><br></div><div>Go:</div><div> cmd := exec.Command("ls", "-l")</div><div> out, err := cmd.CombinedOutput()</div><div><br></div><div>Possible Swift:</div><div> let proc = try Process.run(["ls", "-l"], combinedOutput: true)</div><div> let out = proc.stdout.read()</div><div><br></div><div><br></div><div>4) Shell out</div><div><br></div><div>Python:</div><div> subprocess.check_output(["ls", "-l"], stderr=subprocess.STDOUT, shell=True)</div><div><br></div><div>Go:</div><div> cmd := exec.Command("sh", "-c", "ls -l")</div><div> out, err := cmd.CombinedOutput()</div><div><br></div><div>Possible Swift:</div><div> let proc = try Process.run(["sh", "-c", "ls -l"], combinedOutput: true)</div><div> let out = proc.stdout.read()</div><div><br></div><div><br></div><div>5) Pipe to stdin</div><div><br></div><div>Python:</div><div> p = subprocess.Popen(["wc"], stdin=subprocess.PIPE, stdout=subprocess.PIPE)</div><div> p.stdin.write(b'blah')</div><div> p.stdin.close()</div><div> out = p.stdout.read()</div><div><br></div><div>Go:</div><div> cmd := exec.Command("wc")</div><div> stdin, err := cmd.StdinPipe()</div><div> io.WriteString(stdin, "blah")</div><div> out = cmd.CombinedOutput()</div><div><br></div><div>Possible Swift:</div><div> let stdin = InputStream(data: "blah".data(using: .utf8))</div><div> let proc = try Process.run(["wc"], stdin: stdin, combinedOutput: true)</div><div> let out = proc.stdout.read()</div><div><br></div><div>6) Async</div><div><br></div><div>Python:</div><div> p = subprocess.Popen(["sleep", "5"])</div><div> p.wait()</div><div><br></div><div>Go:</div><div> cmd := exec.Command("sleep", "5")</div><div> err := cmd.Start()</div><div> err2 := cmd.Wait()</div><div><br></div><div>Possible Swift:</div><div> let proc = Process(["sleep", "5"])</div><div> try proc.start()</div><div> try proc.wait()</div><div><br></div></div><div class="gmail_extra"><br><div class="gmail_quote">On Fri, Nov 17, 2017 at 9:34 PM, Tony Parker via swift-corelibs-dev <span dir="ltr"><<a href="mailto:swift-corelibs-dev@swift.org" target="_blank">swift-corelibs-dev@swift.org</a>></span> wrote:<br><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">Hi Abhi,<div><br></div><div>It does seem like there is a possibility of some better convenience API here.</div><div><br></div><div>Any ideas on what form it would take? A class method on Process that returns the output, maybe?</div><div><br></div><div>- Tony<br><div><br><blockquote type="cite"><div><div class="h5"><div>On Nov 16, 2017, at 3:34 PM, Abhi Beckert via swift-corelibs-dev <<a href="mailto:swift-corelibs-dev@swift.org" target="_blank">swift-corelibs-dev@swift.org</a>> wrote:</div><br class="m_-3069825369200109882Apple-interchange-newline"></div></div><div><div><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">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></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"><br></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"><div>#!/usr/bin/php<br></div><div><?<br></div><div><br></div><div>$files = `find ~/Desktop -name *.png`;<br></div><div><br></div><div>foreach (explode("\n", $files) as $file) {<br></div><div> // do something with $file<br></div><div>}<br></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"><br></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">-<br></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"><br></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"><div>#!/usr/bin/swift<br></div><div><br></div><div>import Foundation<br></div><div><br></div><div>let process = Process()<br></div><div>process.launchPath = "/usr/bin/find"<br></div><div>process.arguments = [<br></div><div> NSString(string:"~/Desktop").<wbr>expandingTildeInPath,<br></div><div> "-name",<br></div><div> "*.png"<br></div><div>]<br></div><div><br></div><div>let output = Pipe()<br></div><div>process.standardOutput = output<br></div><div><br></div><div>process.launch()<br></div><div><br></div><div>let files: String<br></div><div>if let filesUtf8 = NSString(data: output.fileHandleForReading.<wbr>readDataToEndOfFile(), encoding: String.Encoding.utf8.rawValue) {<br></div><div> files = filesUtf8 as String<br></div><div>} else {<br></div><div> files = NSString(data: output.fileHandleForReading.<wbr>readDataToEndOfFile(), encoding: String.Encoding.isoLatin1.<wbr>rawValue) as NSString! as String<br></div><div>}<br></div><div><br></div><div>files.enumerateLines { file, _ in<br></div><div> // do something with file<br></div><div>}<br></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"><br></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">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></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"><br></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">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></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"><br></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">- 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">______________________________<wbr>_________________</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"><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">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"><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">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"><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">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>corelibs-dev</a></div></blockquote></div><br></div></div><br>______________________________<wbr>_________________<br>
swift-corelibs-dev mailing list<br>
<a href="mailto:swift-corelibs-dev@swift.org">swift-corelibs-dev@swift.org</a><br>
<a href="https://lists.swift.org/mailman/listinfo/swift-corelibs-dev" rel="noreferrer" target="_blank">https://lists.swift.org/<wbr>mailman/listinfo/swift-<wbr>corelibs-dev</a><br>
<br></blockquote></div><br></div>