1 /** 2 * Simple execution of shell commands, 3 * and wrappers for common utilities. 4 * 5 * License: 6 * This Source Code Form is subject to the terms of 7 * the Mozilla Public License, v. 2.0. If a copy of 8 * the MPL was not distributed with this file, You 9 * can obtain one at http://mozilla.org/MPL/2.0/. 10 * 11 * Authors: 12 * Vladimir Panteleev <vladimir@thecybershadow.net> 13 */ 14 15 module ae.sys.cmd; 16 17 import core.thread; 18 19 import std.exception; 20 import std.process; 21 import std.stdio; 22 import std.string; 23 24 import ae.sys.file; 25 26 string getTempFileName(string extension) 27 { 28 import std.random; 29 import std.file; 30 import std.path : buildPath; 31 32 static int counter; 33 return buildPath(tempDir(), format("run-%d-%d-%d.%s", 34 getpid(), 35 uniform!uint(), 36 counter++, 37 extension 38 )); 39 } 40 41 // ************************************************************************ 42 43 private void invoke(alias runner)(string[] args) 44 { 45 //debug scope(failure) std.stdio.writeln("[CWD] ", getcwd()); 46 auto status = runner(); 47 enforce(status == 0, 48 "Command %s failed with status %d".format(args, status)); 49 } 50 51 /// std.process helper. 52 /// Run a command, and throw if it exited with a non-zero status. 53 void run(string[] args) 54 { 55 invoke!({ return spawnProcess(args).wait(); })(args); 56 } 57 58 /// std.process helper. 59 /// Run a command and collect its output. 60 /// Throw if it exited with a non-zero status. 61 string query(string[] args) 62 { 63 string output; 64 invoke!({ 65 auto result = execute(args); 66 output = result.output.stripRight(); 67 return result.status; 68 })(args); 69 return output; 70 } 71 72 /// std.process helper. 73 /// Run a command, feed it the given input, and collect its output. 74 /// Throw if it exited with non-zero status. Return output. 75 T[] pipe(T)(string[] args, in T[] input) 76 { 77 T[] output; 78 invoke!({ 79 auto pipes = pipeProcess(args); 80 auto f = pipes.stdin; 81 auto writer = writeFileAsync(f, input); 82 scope(exit) writer.join(); 83 output = cast(T[])readFile(pipes.stdout); 84 return pipes.pid.wait(); 85 })(args); 86 return output; 87 } 88 89 // ************************************************************************ 90 91 ubyte[] iconv(in void[] data, string inputEncoding, string outputEncoding) 92 { 93 auto args = ["iconv", "-f", inputEncoding, "-t", outputEncoding]; 94 auto result = pipe(args, data); 95 return cast(ubyte[])result; 96 } 97 98 string iconv(in void[] data, string inputEncoding) 99 { 100 import std.utf; 101 auto result = cast(string)iconv(data, inputEncoding, "UTF-8"); 102 validate(result); 103 return result; 104 } 105 106 version (HAVE_UNIX) 107 unittest 108 { 109 assert(iconv("Hello"w, "UTF-16LE") == "Hello"); 110 } 111 112 string sha1sum(in void[] data) 113 { 114 auto output = cast(string)pipe(["sha1sum", "-b", "-"], data); 115 return output[0..40]; 116 } 117 118 version (HAVE_UNIX) 119 unittest 120 { 121 assert(sha1sum("") == "da39a3ee5e6b4b0d3255bfef95601890afd80709"); 122 assert(sha1sum("a b\nc\r\nd") == "667c71ffe2ac8a4fe500e3b96435417e4c5ec13b"); 123 } 124 125 // ************************************************************************ 126 127 version (Windows) 128 enum NULL_FILE = "nul"; 129 else 130 enum NULL_FILE = "/dev/null"; 131 132 // ************************************************************************ 133 134 /// Reverse of std.process.environment.toAA 135 void setEnvironment(string[string] env) 136 { 137 foreach (k, v; env) 138 if (k.length) 139 environment[k] = v; 140 foreach (k, v; environment.toAA()) 141 if (k.length && k !in env) 142 environment.remove(k); 143 }