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).