github.com/lmorg/murex@v0.0.0-20240217211045-e081c89cd4ef/docs/tour.md (about)

     1  <h1>Language Tour</h1>
     2  
     3  <h2>Table of Contents</h2>
     4  
     5  <div id="toc">
     6  
     7  - [Introduction](#introduction)
     8    - [Read–Eval–Print Loop](#readevalprint-loop)
     9    - [Barewords](#barewords)
    10    - [Expressions and Statements](#expressions-and-statements)
    11    - [Functions and Methods](#functions-and-methods)
    12    - [The Bang Prefix](#the-bang-prefix)
    13  - [Rosetta Stone](#rosetta-stone)
    14  - [Basic Syntax](#basic-syntax)
    15    - [Quoting Strings](#quoting-strings)
    16    - [Code Comments](#code-comments)
    17  - [Variables](#variables)
    18    - [Global variables](#global-variables)
    19    - [Environmental Variables](#environmental-variables)
    20    - [Type Inference](#type-inference)
    21    - [Scalars](#scalars)
    22    - [Arrays](#arrays)
    23  - [Piping and Redirection](#piping-and-redirection)
    24    - [Pipes](#pipes)
    25    - [Redirection](#redirection)
    26    - [Redirecting to files](#redirecting-to-files)
    27    - [Type Conversion](#type-conversion)
    28      - [Cast](#cast)
    29      - [Format](#format)
    30  - [Sub-Shells](#sub-shells)
    31  - [Filesystem Wildcards (Globbing)](#filesystem-wildcards-globbing)
    32  - [Brace expansion](#brace-expansion)
    33  - [Executables](#executables)
    34    - [Aliases](#aliases)
    35    - [Public Functions](#public-functions)
    36    - [Private Functions](#private-functions)
    37    - [External Executables](#external-executables)
    38  - [Control Structures](#control-structures)
    39    - [Using `if` Statements](#using-if-statements)
    40    - [Using `switch` Statements](#using-switch-statements)
    41    - [Using `foreach` Loops](#using-foreach-loops)
    42    - [Using `formap` Loops](#using-formap-loops)
    43  - [Stopping Execution](#stopping-execution)
    44    - [The `continue` Statement](#the-continue-statement)
    45    - [The `break` Statement](#the-break-statement)
    46    - [The `return` Statement](#the-return-statement)
    47    - [The `exit` Statement](#the-exit-statement)
    48    - [Signal: SIGINT](#signal-sigint)
    49    - [Signal: SIGQUIT](#signal-sigquit)
    50    - [Signal: SIGTSTP](#signal-sigtstp)
    51  
    52  </div>
    53  
    54  ## Introduction
    55  
    56  Murex is a typed shell. By this we mean it still passes byte streams along
    57  POSIX pipes (and thus will work with all your existing command line tools) but
    58  in addition will add annotations to describe the type of data that is being
    59  written and read. This allows Murex to expand upon your command line tools
    60  with some really interesting and advanced features not available in traditional
    61  shells.
    62  
    63  > POSIX is a set of underlying standards that Linux, macOS and various other
    64  > operating systems support. Most typed shells do not work well with existing
    65  > commands whereas Murex does.
    66  
    67  ### Read–Eval–Print Loop
    68  
    69  
    70  
    71  If you want to learn more about the interactive shell then there is a dedicated
    72  document detailing [Murex's REPL features](/docs/user-guide/interactive-shell.md).
    73  
    74  ### Barewords
    75  
    76  Shells need to [balance scripting with an efficient interactive terminal](/docs/blog/split_personalities.md)
    77  interface. One of the most common approaches to solving that conflict between
    78  readability and terseness is to make heavy use of barewords. Barewords are
    79  ostensibly just instructions that are not quoted. In our case, command names
    80  and command parameters.
    81  
    82  Murex also makes heavy use of barewords and so that places requirements on
    83  the choice of syntax we can use.
    84  
    85  ### Expressions and Statements
    86  
    87  An **expression** is an evaluation, operation or assignment, for example:
    88  ```
    89  » 6 > 5
    90  » fruit = %[ apples oranges bananas ]
    91  » 5 + 5
    92  ```
    93  
    94  > Expressions are type sensitive
    95  
    96  Whereas a **statement** is a shell command to execute:
    97  ```
    98  » echo "Hello Murex"
    99  » kill 1234
   100  ```
   101  
   102  > All values in a statement are treated as strings
   103  
   104  Due to the expectation of shell commands supporting bareword parameters,
   105  expressions have to be parsed differently to statements. Thus Murex first
   106  parses a command line to see if it is a valid expression, and if it is not, it
   107  then assumes it is an statement and parses it as such.
   108  
   109  This allow expressions and statements to be used interchangeably in a pipeline:
   110  ```
   111  » 5 + 5 | grep 10
   112  ```
   113  
   114  ### Functions and Methods
   115  
   116  A **function** is command that doesn't take data from STDIN whereas a **method**
   117  is any command that does.
   118  ```
   119  echo "Hello Murex" | grep "Murex"
   120  ^ a function         ^ a method
   121  ```
   122  
   123  In practical terms, functions and methods are executed in exactly the same way
   124  however some builtins might behave differently depending on whether values are
   125  passed via STDIN or as parameters. Thus you will often find references to
   126  functions and methods, and sometimes for the same command, within these
   127  documents.
   128  
   129  ### The Bang Prefix
   130  
   131  Some Murex builtins support a bang prefix. This prefix alters the behavior of
   132  those builtins to perform the conceptual opposite of their primary role.
   133  
   134  For example, you could grep a file with `regexp 'm/(dogs|cats)/'` but then you
   135  might want to exclude any matches by using `!regexp 'm/(dogs|cats)/'` instead.
   136  
   137  The details for each supported bang prefix will be in the documents for their
   138  respective builtin.
   139  
   140  ## Rosetta Stone
   141  
   142  If you already know Bash and looking for the equivalent syntax in Murex, then
   143  our [Rosetta Stone](/docs/user-guide/rosetta-stone.md) reference will help you to
   144  translate your Bash code into Murex code.
   145  
   146  ## Basic Syntax
   147  
   148  ### Quoting Strings
   149  
   150  > It is important to note that all strings in expressions are quoted whereas
   151  > strings in statements can be barewords.
   152  
   153  There are three ways to quote a string in Murex:
   154  
   155  * `'single quote'`: use this for string literals    ([read more](/docs/parser/single-quote.md))
   156  * `"double quote"`: use this for infixing variables ([read more](/docs/parser/double-quote.md))
   157  * `%(brace quote)`: use this for nesting quotes     ([read more](/docs/parser/brace-quote.md))
   158  
   159  ### Code Comments
   160  
   161  You can comment out a single like, or end of a line with `#`:
   162  ```
   163  # this is a comment
   164  
   165  echo Hello Murex # this is also a comment
   166  ```
   167  
   168  Multiple lines or mid-line comments can be achieved with `/#` and `#/` tokens:
   169  ```
   170  /#
   171  This is
   172  a multi-line
   173  command
   174  #/
   175  ```
   176  ...which can also be inlined...
   177  ```
   178  » echo Hello /# comment #/ Murex
   179  ```
   180  
   181  (`/#` was chosen because it is similar to C-style comments however `/*` is a
   182  valid glob so Murex has substituted the asterisks with a hash symbol instead)
   183  
   184  ## Variables
   185  
   186  All variables can be defined as expressions and their data types are inferred:
   187  
   188  * `name = "bob"`
   189  * `age = 20 * 2`
   190  * `fruit = %[ apples oranges bananas ]`
   191  
   192  If any variables are unset then reading from them will produce an error (under
   193  Murex's default behavior):
   194  ```
   195  » echo $foobar
   196  Error in `echo` (1,1): variable 'foobar' does not exist
   197  ```
   198  
   199  ### Global variables
   200  
   201  Global variables can be defined using the `$GLOBAL` namespace:
   202  ```
   203  » $GLOBAL.foo = "bar"
   204  ```
   205  
   206  You can also force Murex to read the global assignment of `$foo` (ignoring
   207  any local assignments, should they exist) using the same syntax. eg:
   208  ```
   209  » $GLOBAL.name = "Tom"
   210  » out $name
   211  Tom
   212  
   213  » $name = "Sally"
   214  » out $GLOBAL.name
   215  Tom
   216  » out $name
   217  Sally
   218  ```
   219  
   220  ### Environmental Variables
   221  
   222  Environmental Variables are like global variables except they are copied to any
   223  other programs that are launched from your shell session.
   224  
   225  Environmental variables can be assigned using the `$ENV` namespace:
   226  ```
   227  » $ENV.foo = "bar"
   228  ```
   229  as well as using the `export` statement like with traditional shells. ([read more](/docs/commands/export.md))
   230  
   231  Like with global variables, you can force Murex to read the environmental
   232  variable, bypassing and local or global variables of the same name, by also
   233  using the `$ENV` namespace prefix.
   234  
   235  ### Type Inference
   236  
   237  In general, Murex will try to infer the data type of a variable or pipe. It
   238  can do this by checking the `Content-Type` HTTP header, the file name
   239  extension or just looking at how that data was constructed (when defined via
   240  expressions). However sometimes you may need to annotate your types. ([read more](/docs/commands/set.md#type-annotations))
   241  
   242  ### Scalars
   243  
   244  In traditional shells, variables are expanded in a way that results in spaces
   245  be parsed as different command parameters. This results in numerous problems
   246  where developers need to remember to enclose variables inside quotes.
   247  
   248  Murex parses variables as tokens and expands them into the command line
   249  arguments intuitively. So, there are no more accidental bugs due to spaces in
   250  file names, or other such problems due to developers forgetting to quote
   251  variables. For example:
   252  ```
   253  » file = "file name.txt"
   254  » touch $file # this would normally need to be quoted
   255  » ls
   256  'file name.txt'
   257  ```
   258  ### Arrays
   259  
   260  Due to variables not being expanded into arrays by default, Murex supports an
   261  additional variable construct for arrays. These are `@` prefixed:
   262  ```
   263  » files = %[file1.txt, file2.txt, file3.txt]
   264  » touch @files
   265  » ls
   266  file1.txt  file2.txt
   267  ```
   268  
   269  ## Piping and Redirection
   270  
   271  ### Pipes
   272  
   273  Murex supports multiple different pipe tokens. The main two being `|` and
   274  `->`.
   275  
   276  * `|` works exactly the same as in any normal shell. ([read more](/docs/parser/pipe-posix.md))
   277  
   278  * `->` displays all of the supported methods (commands that support the output
   279    of the previous command). Think of it a little like object orientated
   280    programming where an object will have functions (methods) attached. ([read more](/docs/parser/pipe-arrow.md))
   281  
   282  In Murex scripts you can use `|` and `->` interchangeably, so there's no need
   283  to remember which commands are methods and which are not. The difference only
   284  applies in the interactive shell where `->` can be used with tab-autocompletion
   285  to display a shortlist of supported functions that can manipulate the data from
   286  the previous command. It's purely a clue to the parser to generate different
   287  autocompletion suggestions to help with your discovery of different command
   288  line tools.
   289  
   290  ### Redirection
   291  
   292  Redirection of stdout and stderr is very different in Murex. There is no
   293  support for the `2>` or `&1` tokens,  instead you name the pipe inside angle
   294  brackets, in the first parameter(s).
   295  
   296  `out` is that processes stdout (fd1), `err` is that processes stderr (fd2), and
   297  `null` is the equivalent of piping to `/dev/null`.
   298  
   299  Any pipes prefixed by a bang means reading from that processes stderr.
   300  
   301  So to redirect stderr to stdout you would use `<!out>`:
   302  ```
   303  err <!out> "error message redirected to stdout"
   304  ```
   305  
   306  And to redirect stdout to stderr you would use `<err>`:
   307  ```
   308  out <err> "output redirected to stderr"
   309  ```
   310  
   311  Likewise you can redirect either stdout, or stderr to `/dev/null` via `<null>`
   312  or `<!null>` respectively.
   313  ```
   314  command <!null> # ignore stderr
   315  command <null>  # ignore stdout
   316  ```
   317  
   318  You can also create your own pipes that are files, network connections, or any
   319  other custom data input or output endpoint. ([read more](/docs/user-guide/namedpipes.md))
   320  
   321  ### Redirecting to files
   322  
   323  ```
   324  out "message" |> truncate-file.txt
   325  out "message" >> append-file.txt
   326  ```
   327  
   328  ### Type Conversion
   329  
   330  Aside from annotating variables upon definition, you can also transform data
   331  along the pipeline.
   332  
   333  #### Cast
   334  
   335  Casting doesn't alter the data, it simply changes the meta-information about
   336  how that data should be read.
   337  ```
   338  out [1,2,3] | cast json | foreach { ... }
   339  ```
   340  
   341  There is also a little syntactic sugar to do the same:
   342  ```
   343  out [1,2,3] | :json: foreach { ... }
   344  ```
   345  
   346  #### Format
   347  
   348  `format` takes the source data and reformats it into another data format:
   349  ```
   350  » out [1,2,3] | :json: format yaml
   351  - 1
   352  - 2
   353  - 3
   354  ```
   355  
   356  ## Sub-Shells
   357  
   358  There are two types of emendable sub-shells: strings and arrays.
   359  
   360  * string sub-shells, `${ command }`, take the results from the sub-shell and
   361    return it as a single parameter. This saves the need to encapsulate the shell
   362    inside quotation marks.
   363  
   364  * array sub-shells, `@{ command }`, take the results from the sub-shell
   365    and expand it as parameters.
   366  
   367  **Examples:**
   368  
   369  ```
   370  touch ${ %[1,2,3] } # creates a file named '[1,2,3]'
   371  touch @{ %[1,2,3] } # creates three files, named '1', '2' and '3'
   372  ```
   373  
   374  The reason Murex breaks from the POSIX tradition of using backticks and
   375  parentheses is because Murex works on the principle that everything inside
   376  a curly bracket is considered a new block of code.
   377  
   378  ## Filesystem Wildcards (Globbing)
   379  
   380  While glob expansion is supported in the interactive shell, there isn't
   381  auto-expansion of globbing in shell scripts. This is to protect against
   382  accidental damage. Instead globbing is achieved via sub-shells using either:
   383  
   384  * `g`  - traditional globbing                      ([read more](/docs/commands/g.md))
   385  * `rx` - regexp matching in current directory only ([read more](/docs/commands/rx.md))
   386  * `f`  - file type matching                        ([read more](/docs/commands/f.md))
   387  
   388  **Examples:**
   389  
   390  All text files via globbing:
   391  ```
   392  g *.txt
   393  ```
   394  
   395  All text and markdown files via regexp:
   396  ```
   397  rx '\.(txt|md)$'
   398  ```
   399  
   400  All directories via type matching:
   401  ```
   402  f +d
   403  ```
   404  
   405  You can also chain them together, eg all directories named `*.txt`:
   406  ```
   407  g *.txt | f +d
   408  ```
   409  
   410  To use them in a shell script it could look something a like this:
   411  ```
   412  rm @{g *.txt | f +s}
   413  ```
   414  (this deletes any symlinks called `*.txt`)
   415  
   416  ## Brace expansion
   417  
   418  In [bash you can expand lists](https://en.wikipedia.org/wiki/Bash_(Unix_shell)#Brace_expansion)
   419  using the following syntax: `a{1..5}b`. In Murex, like with globbing, brace
   420  expansion is a function: `a a[1..5]b` and supports a much wider range of lists
   421  that can be expanded. ([read more](/docs/commands/a.md))
   422  
   423  ## Executables
   424  
   425  ### Aliases
   426  
   427  You can create "aliases" to common commands to save you a few keystrokes. For
   428  example:
   429  ```
   430  alias gc=git commit
   431  ```
   432  
   433  `alias` behaves slightly differently to Bash. ([read more](/docs/commands/alias.md))
   434  
   435  ### Public Functions
   436  
   437  You can create custom functions in Murex using `function`. ([read more](/docs/commands/function.md))
   438  ```
   439  function gc (message: str) {
   440      # shorthand for `git commit`
   441      
   442      git commit -m $message
   443  }
   444  ```
   445  
   446  ### Private Functions
   447  
   448  `private` functions are like [public functions](#public-functions) except they
   449  are only available within their own modules namespace. ([read more](/docs/commands/private.md))
   450  
   451  ### External Executables
   452  
   453  External executables (including any programs located in `$PATH`) are invoked
   454  via the `exec` builtin ([read more](/docs/commands/exec.md)) however if a command
   455  isn't an expression, alias, function nor builtin, then Murex assumes it is an
   456  external executable and automatically invokes `exec`.
   457  
   458  For example the two following statements are the same:
   459  
   460  1. `exec uname`
   461  2. `uname`
   462  
   463  Thus for normal day to day usage, you shouldn't need to include `exec`.
   464  
   465  ## Control Structures
   466  
   467  ### Using `if` Statements
   468  
   469  `if` can be used in a number of different ways, the most common being:
   470  ```
   471  if { true } then {
   472      # do something
   473  } else {
   474      # do something else
   475  }
   476  ```
   477  
   478  `if` supports a flexible variety of incarnation to solve different problems. ([read more](/docs/commands/if.md))
   479  
   480  ### Using `switch` Statements
   481  
   482  Because `if ... else if` chains are ugly, Murex supports `switch` statements:
   483  ```
   484  switch $USER {
   485      case "Tom"   { out "Hello Tom" }
   486      case "Dick"  { out "Howdie Richard" }
   487      case "Sally" { out "Nice to meet you" }
   488  
   489      default {
   490          out "I don't know who you are"
   491      }
   492  }
   493  ```
   494  
   495  `switch` supports a flexible variety of different usages to solve different
   496  problems. ([read more](/docs/commands/switch.md))
   497  
   498  ### Using `foreach` Loops
   499  
   500  `foreach` allows you to easily iterate through an array or list of any type: ([read more](/docs/commands/foreach.md))
   501  ```
   502  %[ apples bananas oranges ] | foreach fruit { out "I like $fruit" }
   503  ```
   504  
   505  ### Using `formap` Loops
   506  
   507  `formap` loops are the equivalent of `foreach` but against map objects: ([read more](/docs/commands/formap.md))
   508  ```
   509  %{
   510      Bob:     {age: 10},
   511      Richard: {age: 20},
   512      Sally:   {age: 30}
   513  } | formap name person {
   514      out "$name is $person[age] years old"
   515  }
   516  ```
   517  
   518  ## Stopping Execution
   519  
   520  ### The `continue` Statement
   521  
   522  `continue` will terminate execution of an inner block in iteration loops like
   523  `foreach` and `formap`. Thus _continuing_ the loop from the next iteration:
   524  ```
   525  %[1..10] | foreach i {
   526      if { $i == 5 } then {
   527          continue foreach
   528          # ^ jump back to the next iteration
   529      }
   530  
   531      out $i
   532  }
   533  ```
   534  
   535  `continue` requires a parameter to define while block to iterate on. This means
   536  you can use `continue` within nested loops and still have readable code. ([read more](/docs/commands/continue.md))
   537  
   538  ### The `break` Statement
   539  
   540  `break` will terminate execution of a block (eg `function`, `private`, `if`,
   541  `foreach`, etc):
   542  ```
   543  %[1..10] | foreach i {
   544      if { $i == 5 } then {
   545          break foreach
   546          # ^ exit foreach
   547      }
   548  
   549      out $i
   550  }
   551  ```
   552  
   553  `break` requires a parameter to define while block to end. Thus `break` can be
   554  considered to exhibit the behavior of _return_ as well as _break_ in other
   555  languages:
   556  ```
   557  function example {
   558      if { $USER == "root" } then {
   559          err "Don't run this as root"
   560          break example
   561      }
   562  
   563      # ... do something ...
   564  }
   565  ```
   566  
   567  `break` cannot exit anything above it's callers scope. ([read more](/docs/commands/break.md))
   568  
   569  ### The `return` Statement
   570  
   571  `return` ends the current scope (typically a function). ([read more](/docs/commands/return.md))
   572  
   573  ### The `exit` Statement
   574  
   575  `exit` terminates Murex. It is not scope aware; if it is included in a function
   576  then the whole shell will still exist and not just that function. ([read more](/docs/commands/exit.md))
   577  
   578  ### Signal: SIGINT
   579  
   580  This can be invoked by pressing `Ctrl` + `c`.
   581  
   582  This is functionally the same as `fid-kill`. (([read more](/docs/commands/fid-kill.md)))
   583  
   584  ### Signal: SIGQUIT
   585  
   586  This can be invoked by pressing `Ctrl` + `\`. ([read more](/docs/user-guide/terminal-keys.md))
   587  
   588  Sending SIGQUIT will terminate all running functions in the current Murex
   589  session. Which is a handy escape hatch if your shell code starts misbehaving.
   590  
   591  ### Signal: SIGTSTP
   592  
   593  This can be invoked by pressing `Ctrl` + `z`. ([read more](/docs/user-guide/job-control.md))
   594  
   595  ## See Also
   596  
   597  * [Install](/INSTALL.md):
   598    Installation instructions
   599  
   600  <hr/>
   601  
   602  This document was generated from [gen/root/tour_doc.yaml](https://github.com/lmorg/murex/blob/master/gen/root/tour_doc.yaml).