<!DOCTYPE html>
<html>
<head>
<title></title>
<style type="text/css">p.MsoNormal,p.MsoNoSpacing{margin:0}</style>
</head>
<body><blockquote type="cite"><div>Any ideas on what form it would take? A class method on Process that returns the output, maybe?<br></div>
</blockquote><div><br></div>
<div>I think there are two solutions, and I'd like to see both of them taken.<br></div>
<div><br></div>
<div>First of all, I disagree with Brent's assessment that we shouldn't have a variant that "simply shells out". Even though this use case is "discouraged" in other languages, I think in practice it definitely has a place.<br></div>
<div><br></div>
<div>I would like swift to have something similar to PHP's backticks, with language syntax to simply execute a shell command - maybe somethinglike this:<br></div>
<div><br></div>
<blockquote type="cite"><div dir="ltr">let output = #!"ls ~/Desktop -name *.png"</div>
</blockquote><div><br></div>
<div>In the above example, the user's shell PATH variable would be used to find the `ls` command, the tilde in in the path would be expanded, and arguments would be split on whitespace, and output would be string with automatically detected encoding. While this is terrible for complicated use cases, it is appropriate for simple ones.<br></div>
<div><br></div>
<div>For a second solution to handle complex cases, the current API just needs a lot of cleanup work – especially:<br></div>
<div><br></div>
<div>&nbsp;* expanding a tilde should not require working with NSString (perhaps NSURL could have an option to expand tildes when created from a string)<br></div>
<div>&nbsp;* It should be possible to capture the entire output of a process without working with NSPipe and NSFileHandle. Personally I'd prefer a block that is executed on completion with NSData arguments for stdout and stderr, but there are other options.<br></div>
<div>&nbsp;* converting NSData to a swift string should not require working with NSString and it should be possible to do this with automatically detected encoding (NSString can do this when reading from a file, and CFString can do it reading from NSData. Swift strings should have it as well).<br></div>
<div><br></div>
<div>- Abhi</div>
<div><br></div>
<div>On Sat, Nov 18, 2017, at 10:47, Brent Royal-Gordon wrote:<br></div>
<blockquote type="cite"><div><blockquote type="cite"><div>On Nov 17, 2017, at 11:34 AM, Tony Parker via swift-corelibs-dev &lt;<a href="mailto:swift-corelibs-dev@swift.org">swift-corelibs-dev@swift.org</a>&gt; wrote:<br></div>
</blockquote><blockquote type="cite"><br></blockquote><blockquote type="cite">It does seem like there is a possibility of some better convenience API here.<br></blockquote><blockquote type="cite"><div><br></div>
<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;-webkit-text-stroke-width:0px;">Any ideas on what form it would take? A class method on Process that returns the output, maybe?<br></div>
</div>
</blockquote><div><br></div>
</div>
<div>`Process.run(_:arguments:terminationHandler:)` is not a bad basis for this, other than the first argument being a URL. I might add a variant which does a $PATH search and expands tildes:<br></div>
<div><br></div>
<div><span style="white-space:pre;"></span>extension Process {<br></div>
<div><span style="white-space:pre;"></span>class func runInPath(_ commandName: String, with arguments: [String], terminationHandler: ((Process) -&gt; Void)? = nil) -&gt; Process<br></div>
<div><span style="white-space:pre;"></span>}<br></div>
<div><br></div>
<div>(I would *not* add a variant which simply shells out, or at least I wouldn't make it the only option. Every scripting language I can think of has this feature, and every one of them discourages its use and pushes people towards something else with pre-split arguments and no shell attack surface.)<br></div>
<div><br></div>
<div>And then add a method which gathers all stdout output and returns it, along with the termination status (throwing if it's a signal):<br></div>
<div><br></div>
<div><span style="white-space:pre;"></span>extension Process {<br></div>
<div><span style="white-space:pre;"></span>func&nbsp;outputAfterExit() throws -&gt; (output: String, status: Int32)<br></div>
<div><span style="white-space:pre;"></span>}<br></div>
<div><br></div>
<div>The result is not *as* convenient as PHP, but it's a lot safe than running things through a shell, too.<br></div>
<div><br></div>
<div><span style="white-space:pre;"></span>let (output, _) = try&nbsp;Task.runInPath("find", with: ["~/Desktop", "-name", "*.png"]).outputAfterExit()<br></div>
<div><br></div>
<div>The biggest problem I see with this design is that expanding tildes in the arguments, but *not* globbing them, happens to be right for this case, but may not be in the general case. One interesting possibility would be to go the custom operator route:<br></div>
<div><br></div>
<div><span style="white-space:pre;"></span>/// Returns an absolute path placing `relativePath` in the user's home directory.<br></div>
<div><span style="white-space:pre;"></span>prefix func ~ (relativePath: String) -&gt; String {<br></div>
<div><span style="white-space:pre;"></span>return FileManager.default.homeDirectoryForCurrentUser.appendingPathComponent(relativePath).path<br></div>
<div><span style="white-space:pre;"></span>}<br></div>
<div><br></div>
<div><div><span style="white-space:pre;"></span>let (output, _) = try&nbsp;Task.runInPath("find", with: [~"Desktop", "-name", "*.png"]).outputAfterExit()<br></div>
</div>
<div><br></div>
<div>But I'm not sure we'd want that available in million-line Mac apps.<br></div>
<div><br></div>
<div>On the other hand, maybe Foundation's APIs in general are too verbose for scripting. I could imagine a "Foundation.Script" module which added some unprincipled but convenient shortcuts:<br></div>
<div><br></div>
<div><span style="white-space:pre;"></span>extension URL: ExpressibleByStringLiteral {<span style="white-space:pre;"> </span>// Should be interpolatable too, but we need to redesign that.<br></div>
<div><span style="white-space:pre;"></span>public&nbsp;init(stringLiteral value: String) {<br></div>
<div><span style="white-space:pre;"></span>self.init(fileURLWithPath: value)<br></div>
<div><span style="white-space:pre;"></span>}<br></div>
<div><span style="white-space:pre;"></span>}<br></div>
<div><span style="white-space:pre;"></span>public var FS { return FileManager.default }<br></div>
<div><span style="white-space:pre;"></span>public var ENV { return ProcessInfo.processInfo.environment }<br></div>
<div><br></div>
<div>That might be a good place for a leading tilde operator, too.<br></div>
<div><br></div>
<div><div><span style="border-collapse:separate;font-variant-ligatures:normal;font-variant-east-asian:normal;line-height:normal;-webkit-border-horizontal-spacing:0px;-webkit-border-vertical-spacing:0px;"></span><br></div>
<div><div style="font-size:12px;"><span style="border-collapse:separate;font-variant-ligatures:normal;font-variant-east-asian:normal;line-height:normal;-webkit-border-horizontal-spacing:0px;-webkit-border-vertical-spacing:0px;">--&nbsp;</span><br></div>
<div style="font-size:12px;"><span style="border-collapse:separate;font-variant-ligatures:normal;font-variant-east-asian:normal;line-height:normal;-webkit-border-horizontal-spacing:0px;-webkit-border-vertical-spacing:0px;">Brent Royal-Gordon</span><br></div>
<div style="font-size:12px;"><span style="border-collapse:separate;font-variant-ligatures:normal;font-variant-east-asian:normal;line-height:normal;-webkit-border-horizontal-spacing:0px;-webkit-border-vertical-spacing:0px;">Architechies</span><br></div>
</div>
<div><span style="border-collapse:separate;font-variant-ligatures:normal;font-variant-east-asian:normal;line-height:normal;-webkit-border-horizontal-spacing:0px;-webkit-border-vertical-spacing:0px;"></span><br></div>
</div>
</blockquote><div><br></div>
</body>
</html>