1 /** 2 * Utilities for running and interacting with processes. 3 */ 4 module dshutils.processutils; 5 6 import dsh; 7 8 /** 9 * Helper for more easily creating processes. 10 */ 11 public class ProcessBuilder { 12 private File stdin; 13 private File stdout; 14 private File stderr; 15 private string[string] env; 16 private string dir; 17 18 public this() { 19 this.stdin = std.stdio.stdin; 20 this.stdout = std.stdio.stdout; 21 this.stderr = std.stdio.stderr; 22 this.dir = getcwd(); 23 } 24 25 public ProcessBuilder inputFrom(string filename) { 26 this.stdin = File(filename, "r"); 27 return this; 28 } 29 30 public ProcessBuilder outputTo(string filename) { 31 this.stdout = File(filename, "w"); 32 return this; 33 } 34 35 public ProcessBuilder errorTo(string filename) { 36 this.stderr = File(filename, "w"); 37 return this; 38 } 39 40 public ProcessBuilder withEnv(string key, string value) { 41 this.env[key] = value; 42 return this; 43 } 44 45 public ProcessBuilder workingDir(string dir) { 46 this.dir = dir; 47 return this; 48 } 49 50 public int run(string command) { 51 import std.process; 52 import std.regex; 53 try { 54 auto r = regex("\\s+"); 55 auto s = split(command, r); 56 auto pid = spawnProcess(s, this.stdin, this.stdout, this.stderr, this.env, Config.none, this.dir); 57 return wait(pid); 58 } catch (ProcessException e) { 59 error("Could not start process \"%s\": %s", command, e.msg); 60 return -1; 61 } 62 } 63 } 64 65 unittest { 66 auto p = new ProcessBuilder() 67 .outputTo(".test.txt") 68 .workingDir("source"); 69 p.run("ls"); 70 71 } 72 73 /** 74 * Runs the given command using the user's shell. 75 * Params: 76 * cmd = The command to execute. 77 * Returns: The exit code from the command. Generally, 0 indicates success. If 78 * the process could not be started, -1 is returned. 79 */ 80 public int run(string cmd) { 81 return new ProcessBuilder().run(cmd); 82 } 83 84 unittest { 85 print("Running test of run(cmd). Expect some test output!"); 86 version(Posix) { 87 assert(run("ls") == 0); 88 } 89 version(Windows) { 90 assert(run("dir") == 0); 91 } 92 print("Testing run(cmd) with a non-existent command. Expect an error message."); 93 assert(run("kjafhdflkuahlkefuhfahlfeuhaf") == -1); 94 } 95 96 /** 97 * Convenience method to run a command and pipe output to a file. 98 * Params: 99 * cmd = The command to run. 100 * outputFile = The file to send output to. 101 * Returns: The exit code from the command. 102 */ 103 public int run(string cmd, string outputFile) { 104 return new ProcessBuilder() 105 .outputTo(outputFile) 106 .errorTo(outputFile) 107 .run(cmd); 108 } 109 110 /** 111 * Runs the given command, and exits the program if the return code is not 0. 112 * Params: 113 * cmd = The command to run. 114 */ 115 public void runOrQuit(string cmd) { 116 import core.stdc.stdlib : exit; 117 int r = run(cmd); 118 if (r != 0) { 119 error("Process \"%s\" exited with code %d", cmd, r); 120 exit(r); 121 } 122 } 123 124 public void runOrQuit(string cmd, string outputFile) { 125 import core.stdc.stdlib : exit; 126 int r = run(cmd, outputFile); 127 if (r != 0) { 128 error("Process \"%s\" exited with code %d", cmd, r); 129 exit(r); 130 } 131 } 132 133 /** 134 * Gets an environment variable. 135 * Params: 136 * key = The name of the environment variable. 137 * Returns: The value of the environment variable, or null. 138 */ 139 public string getEnv(string key) { 140 import std.process : environment; 141 try { 142 return environment[key]; 143 } catch (Exception e) { 144 return null; 145 } 146 } 147 148 unittest { 149 assert(getEnv("PATH") !is null); 150 assert(getEnv("flkahelkuhfalukfehlakuefhl") is null); 151 } 152 153 /** 154 * Sets an environment variable. 155 * Params: 156 * key = The name of the environment variable. 157 * value = The value to set. 158 */ 159 public void setEnv(string key, string value) { 160 import std.process : environment; 161 try { 162 environment[key] = value; 163 } catch (Exception e) { 164 error("Could not set environment variable \"%s\": %s", key, e.msg); 165 } 166 } 167 168 unittest { 169 assert(getEnv("dsh_test_env_1") is null); 170 setEnv("dsh_test_env_1", "yes"); 171 assert(getEnv("dsh_test_env_1") == "yes"); 172 }