|
142 | 142 | </code> |
143 | 143 | </pre> |
144 | 144 |
|
| 145 | +<h2 id="stream-merging">Merging one stream into the other</h2> |
| 146 | + |
| 147 | +<p> |
| 148 | +Use <code>2>&1</code> to send standard error to wherever standard output ends up, |
| 149 | +or <code>1>&2</code> to send standard output to wherever standard error ends up. |
| 150 | +These are complete tokens: no spaces are allowed inside them. |
| 151 | +Unlike POSIX shells, they are <em>not</em> order-sensitive file descriptor duplication: |
| 152 | +a merge means "this stream goes to the other stream's <em>final</em> destination", |
| 153 | +so there is no <code>> file 2>&1</code> vs <code>2>&1 > file</code> ordering trap. |
| 154 | +Both streams share a single destination, preserving output order, and this works the same on every platform. |
| 155 | +</p> |
| 156 | + |
| 157 | +<pre> |
| 158 | +<code><span class="mshellLEFT_SQUARE_BRACKET">[</span><span class="mshellLITERAL">yourCommand</span><span class="mshellRIGHT_SQUARE_BRACKET">]</span> <span class="mshellSTDERRTOSTDOUT">2>&1</span> <span class="mshellASTERISK">*</span> <span class="mshellBANG">!</span> <span class="mshellLINECOMMENT"># Captures stdout and stderr interleaved as one string.</span> |
| 159 | +<span class="mshellLEFT_SQUARE_BRACKET">[</span><span class="mshellLEFT_SQUARE_BRACKET">[</span><span class="mshellLITERAL">make</span><span class="mshellRIGHT_SQUARE_BRACKET">]</span> <span class="mshellSTDERRTOSTDOUT">2>&1</span> <span class="mshellLEFT_SQUARE_BRACKET">[</span><span class="mshellLITERAL">grep</span> <span class="mshellLITERAL">-i</span> <span class="mshellLITERAL">error</span><span class="mshellRIGHT_SQUARE_BRACKET">]</span><span class="mshellRIGHT_SQUARE_BRACKET">]</span> <span class="mshellPIPE">|</span><span class="mshellEXECUTE">;</span> <span class="mshellLINECOMMENT"># stderr flows through the pipe.</span> |
| 160 | +<span class="mshellLEFT_SQUARE_BRACKET">[</span><span class="mshellLITERAL">yourCommand</span><span class="mshellRIGHT_SQUARE_BRACKET">]</span> <span class="mshellSTDOUTTOSTDERR">1>&2</span> <span class="mshellEXECUTE">;</span> <span class="mshellLINECOMMENT"># stdout appears on stderr, e.g. visible even when the surrounding output is captured.</span> |
| 161 | +<span class="mshellLEFT_SQUARE_BRACKET">[</span><span class="mshellLITERAL">yourCommand</span><span class="mshellRIGHT_SQUARE_BRACKET">]</span> <span class="mshellPATH">`output.log`</span> <span class="mshellGREATERTHAN">></span> <span class="mshellSTDERRTOSTDOUT">2>&1</span> <span class="mshellBANG">!</span> <span class="mshellLINECOMMENT"># Both streams into output.log; equivalent to &>.</span> |
| 162 | +</code> |
| 163 | +</pre> |
| 164 | + |
| 165 | +<h2 id="one-destination-per-stream">Each stream has exactly one destination</h2> |
| 166 | + |
| 167 | +<p> |
| 168 | +A stream's destination can be set once: a file redirect, a capture, an in-place redirect, or a merge. |
| 169 | +Applying a second destination to the same stream is an error, caught both by the static type checker and at runtime. |
| 170 | +For example, <code>[cmd] * `f` ></code> (capture and file redirect on stdout), <code>[cmd] 2>&1 ^</code> (merge and capture on stderr), |
| 171 | +and <code>[cmd] 2>&1 1>&2</code> (circular merge) are all rejected. |
| 172 | +<code>2>&1</code> also cannot be combined with <code><></code>, since stderr text would be written back into the edited file. |
| 173 | +</p> |
| 174 | + |
145 | 175 | <table> |
146 | 176 | <caption>Summary of external command operators</caption> |
147 | 177 | <thead> |
|
161 | 191 | <tr> <td><code>&>></code></td> <td>Redirect both stdout and stderr to a file.</td> <td>Appends to the file.</td> </tr> |
162 | 192 | <tr> <td><code>^</code></td> <td>Capture stderr to the stack.</td> <td>As a string.</td> </tr> |
163 | 193 | <tr> <td><code>^b</code></td> <td>Capture stderr to the stack.</td> <td>As binary.</td> </tr> |
| 194 | + <tr> <td><code>2>&1</code></td> <td>Merge stderr into stdout's destination.</td> <td>Single token, no spaces. Not order-sensitive: stderr follows stdout's final destination.</td> </tr> |
| 195 | + <tr> <td><code>1>&2</code></td> <td>Merge stdout into stderr's destination.</td> <td>Single token, no spaces.</td> </tr> |
164 | 196 | <tr> <td><code><</code></td> <td>Feed stdin from a value.</td> <td>String, path, or binary.</td> </tr> |
165 | 197 | <tr> <td><code><></code></td> <td>In-place file modification.</td> <td>Reads file to stdin, writes stdout back on success.</td> </tr> |
166 | 198 | <tr> <td><code>&</code></td> <td>Mark the command list to run asynchronously.</td> <td>Marks the list; the trailing <code>;</code>/<code>!</code> starts the subprocess and returns immediately without waiting. Stdout and stderr default to discarded.</td> </tr> |
|
170 | 202 | <h2 id="quotation-redirection">Redirection on quotations</h2> |
171 | 203 |
|
172 | 204 | <p> |
173 | | -All of the redirection operators above also work on quotations. |
| 205 | +The redirection operators that don't change the stack also work on quotations: |
| 206 | +file redirects (<code>></code>, <code>>></code>, <code>2></code>, <code>2>></code>, <code>&></code>, <code>&>></code>), |
| 207 | +stdin (<code><</code>), and the merges (<code>2>&1</code>, <code>1>&2</code>). |
174 | 208 | This is useful when you want to redirect the output of mshell code that uses <code>wl</code>, <code>wle</code>, or other built-in functions that write to stdout or stderr. |
175 | 209 | It is also useful when you have many commands that you want to run while appending all the outputs to a single file, without having to put the redirection on each command invocation. |
176 | 210 | </p> |
177 | 211 |
|
| 212 | +<p> |
| 213 | +The captures (<code>*</code>, <code>*b</code>, <code>^</code>, <code>^b</code>) and the in-place redirect (<code><></code>) are <em>not</em> allowed on quotations, |
| 214 | +because they would change the quotation's stack effect. |
| 215 | +Capture the individual command lists inside the quotation instead — for example, run a list of commands and collect each stdout: |
| 216 | +</p> |
| 217 | + |
| 218 | +<pre> |
| 219 | +<code><span class="mshellLEFT_SQUARE_BRACKET">[</span><span class="mshellLEFT_SQUARE_BRACKET">[</span><span class="mshellLITERAL">cmd1</span><span class="mshellRIGHT_SQUARE_BRACKET">]</span> <span class="mshellLEFT_SQUARE_BRACKET">[</span><span class="mshellLITERAL">cmd2</span><span class="mshellRIGHT_SQUARE_BRACKET">]</span><span class="mshellRIGHT_SQUARE_BRACKET">]</span> <span class="mshellLEFT_PAREN">(</span><span class="mshellASTERISK">*</span> <span class="mshellBANG">!</span><span class="mshellRIGHT_PAREN">)</span> <span class="mshellLITERAL">map</span> <span class="mshellLINECOMMENT"># Run each command, collect stdouts as a list of strings.</span> |
| 220 | +</code> |
| 221 | +</pre> |
| 222 | + |
| 223 | +<p> |
| 224 | +Destination conflicts on quotations (e.g. two <code>></code> redirects, or <code>2>&1</code> plus <code>2></code>) are caught at runtime. |
| 225 | +Since redirects never change a quotation's stack effect, they are invisible to the static type checker. |
| 226 | +</p> |
| 227 | + |
| 228 | +<p> |
| 229 | +A quotation's redirect is resolved each time the quotation executes. |
| 230 | +So a <code>></code>-redirected quotation run repeatedly — stored and executed with <code>x</code> several times, or passed to <code>each</code> or <code>map</code> — truncates the file on every execution, leaving only the last run's output. |
| 231 | +Use <code>>></code> when a repeatedly-executed quotation should accumulate output. |
| 232 | +The exception is <code>loop</code>: the file is opened once when the loop starts, so all iterations write to the same open file. |
| 233 | +</p> |
| 234 | + |
178 | 235 | <pre> |
179 | 236 | <code><span class="mshellLEFT_PAREN">(</span> |
180 | 237 | <span class="mshellSTRING">"Hello from stdout"</span> <span class="mshellLITERAL">wl</span> |
|
0 commit comments