Execution of external commands or binaries is different in mshell. Instead of it being the main syntactical construct, in mshell you build up a list of arguments, and then at a time of your choosing, you execute it.
For example:
['my-program' 'arg1' 'arg2'];
Often there are different things you want out of your execution,
or you want different behavior depending on the exit code.
mshell gives you full flexibility to decide with concise syntax.
To initiate execution, you use one of 3 operators:
;: Execute command, always continue. Don't provide any information on the exit code.!: Execute command, but stop the running script execution on any non-zero exit code.?: Execute command, leaving the exit code integer on the stack.
['false']; # mshell will continue past this point
['true']! # Will execute and continue because of 0 exit code
['my-command']? exitCode!
$"Exit code was {@exitCode}" wl
The other choice you often have when executing commands is what to do with the standard output.
Sometimes you will want to redirect it to a file, other times you will want to leave the contents on the stack to process further.
For that, you use the >, >>, *, and *b operators.
[yourCommand] `fileToRedirectTo` > ! # Redirects stdout to the file, truncating it.
[yourCommand] `fileToRedirectTo` >> ! # Redirects stdout to the file, appending.
[yourCommand] * ! # Puts all of stdout on the stack as a string.
[yourCommand] *b ! # Puts all of stdout on the stack as a binary.
You can do similar things with standard error.
To redirect standard error to a file, use 2>
[?],
and ^ instead of *.
To redirect both standard output and standard error to the same file, use &> (truncate) or &>> (append).
This ensures both streams share the same file descriptor, preserving the order of output.
[yourCommand] `output.log` &> ! # Redirects both stdout and stderr to the file, truncating it.
[yourCommand] `output.log` &>> ! # Redirects both stdout and stderr to the file, appending.
If you manually use > and 2> with exactly the same path string, mshell will automatically
use a single file descriptor for both streams, avoiding race conditions. However, if the append modes differ
(e.g., > and 2>> to the same file), an error will be raised.
[yourCommand] `errors.log` 2> ! # Redirects stderr to the file, truncating it.
[yourCommand] `errors.log` 2>> ! # Redirects stderr to the file, appending.
[yourCommand] ^ ! # Puts all of stderr on the stack as a string.
[yourCommand] ^b ! # Puts all of stderr on the stack as a binary.
If you want to put both standard output and standard error onto the stack, you can do that. Standard output will always be pushed first, and then standard error.
So the following are equivalent:
[yourCommand] *b ^b ! # Order here does not matter
[yourCommand] ^b *b !
| Operator | Effect on external commands | Notes |
|---|---|---|
; | Execute and continue. | No exit code on the stack. |
! | Execute and stop on non-zero exit. | Uses the command exit code. |
? | Execute and push the exit code. | Integer is left on the stack. |
> | Redirect stdout to a file. | Truncates the file. |
>> | Redirect stdout to a file. | Appends to the file. |
* | Capture stdout to the stack. | As a string. |
*b | Capture stdout to the stack. | As binary. |
2> | Redirect stderr to a file. | Truncates the file. |
2>> | Redirect stderr to a file. | Appends to the file. |
&> | Redirect both stdout and stderr to a file. | Truncates the file. |
&>> | Redirect both stdout and stderr to a file. | Appends to the file. |
^ | Capture stderr to the stack. | As a string. |
^b | Capture stderr to the stack. | As binary. |
< | Feed stdin from a value. | String, path, or binary. |
<> | In-place file modification. | Reads file to stdin, writes stdout back on success. |
All of the redirection operators above also work on quotations.
This is useful when you want to redirect the output of mshell code that uses wl, wle, or other built-in functions that write to stdout or stderr.
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.
(
"Hello from stdout" wl
"Hello from stderr" wle
) `output.log` &> x # Redirects both stdout and stderr from the quotation to output.log
(
[echo "Running step 1"]!
[echo "Running step 2"]!
[echo "Running step 3"]!
) `build.log` >> x # All command outputs appended to build.log
Use < to feed data into stdin. The type of the value on top of the stack determines how the input is provided.
String values are encoded as UTF-8 and streamed as text.
[wc -l] "line 1\nline 2\n" < ; # Counts the lines from the provided string
Path values open the referenced file and stream its contents.
[wc -l] `myfile.txt` < ; # Equivalent to shell input redirection from a file
Binary values are written directly without any string conversion.
[md5sum] `binary_stdin.bin` readFileBytes < ; # Streams raw bytes into the command
The <> operator enables in-place file modification. It reads a file's contents,
passes them to the command's stdin, and on successful completion (exit code 0), writes the
command's stdout back to the same file. This is similar to the sponge command
from moreutils.
[sort -u] `file.txt` <> !
This is equivalent to, but safer than:
[sort -u] `file.txt` < `file.txt.tmp` > !
[mv file.txt.tmp file.txt] !
<> is evaluated, capturing its contents at that moment.Path type (backtick-quoted), not a string.<> is evaluated.
# Sort a file in place, removing duplicates
[sort -u] `data.txt` <> !
# Format JSON in place
[jq .] `config.json` <> !
# Filter lines in place (keep only lines containing "error")
[grep error] `log.txt` <> !
# Using with ? to check exit code without failing
[sort -u] `file.txt` <> ?
0 = if
"File sorted successfully" wl
else
"Sort failed, file unchanged" wl
end
When executing an external command, mshell resolves the binary name using a two-step process.
First it checks the bin map file for an override, and if none is found it falls back to PATH.
The bin map file lives alongside the history files (for example, ~/.local/share/msh/msh_bins.txt on Linux/macOS or %LOCALAPPDATA%\mshell\msh_bins.txt on Windows).
Each non-empty line is a tab-separated pair of fields: the binary name and the absolute path to the binary.
Both fields are trimmed, the name must not contain path separators, and the line must contain exactly two fields.
mytool /usr/local/bin/mytool
another /home/me/bin/another
To manage the file, use the msh bin subcommands:
msh bin add <path>: add/replace using the file basename and absolute pathmsh bin add <name> <path>: add/replace using an explicit namemsh bin remove <name>: remove an entry by namemsh bin list: print the bin map file contentsmsh bin path: print the bin map file pathmsh bin edit: edit the file in $EDITORmsh bin audit: report invalid or missing entriesmsh bin debug <name>: show lookup details for a binary