github.com/madlambda/nash@v0.2.2-0.20230113003044-f2284521680b/README.md (about)

     1  <!-- mdtocstart -->
     2  
     3  # Table of Contents
     4  
     5  - [nash](#nash)
     6  - [Show time!](#show-time)
     7      - [Useful stuff](#useful-stuff)
     8      - [Why nash scripts are reliable?](#why-nash-scripts-are-reliable)
     9      - [Installation](#installation)
    10          - [Release](#release)
    11              - [Linux](#linux)
    12          - [Master](#master)
    13      - [Getting started](#getting-started)
    14  - [Accessing command line args](#accessing-command-line-args)
    15  - [Namespace features](#namespace-features)
    16  - [OK, but how scripts should look like?](#ok-but-how-scripts-should-look-like)
    17  - [Didn't work?](#didnt-work)
    18  - [Language specification](#language-specification)
    19  - [Some Bash comparisons](#some-bash-comparisons)
    20  - [Security](#security)
    21  - [Installing libraries](#installing-libraries)
    22  - [Releasing](#releasing)
    23  - [Want to contribute?](#want-to-contribute)
    24  
    25  <!-- mdtocend -->
    26  
    27  # nash
    28  
    29  [![Join the chat at https://gitter.im/madlambda/nash](https://badges.gitter.im/madlambda/nash.svg)](https://gitter.im/madlambda/nash?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![GoDoc](https://godoc.org/github.com/madlambda/nash?status.svg)](https://godoc.org/github.com/madlambda/nash)
    30  [![Build Status](https://travis-ci.org/madlambda/nash.svg?branch=master)](https://travis-ci.org/madlambda/nash) [![Go Report Card](https://goreportcard.com/badge/github.com/madlambda/nash)](https://goreportcard.com/report/github.com/madlambda/nash)
    31  
    32  Nash is a system shell, inspired by plan9 `rc`, that makes it easy to create reliable and safe scripts taking advantages of operating systems namespaces (on linux and plan9) in an idiomatic way.
    33  
    34  
    35  ## Useful stuff
    36  
    37  - nashfmt: Formats nash code (like gofmt) but no code styling defined yet (see Installation section).
    38  - [nashcomplete](https://github.com/madlambda/nashcomplete): Autocomplete done in nash script.
    39  - [Dotnash](https://github.com/lborguetti/dotnash): Nash profile customizations (e.g: prompt, aliases, etc)
    40  - [nash-mode](https://github.com/tiago4orion/nash-mode.el): Emacs major mode integrated with `nashfmt`.
    41  
    42  ## Why nash scripts are reliable?
    43  
    44  1. Nash aborts at first non-success status of commands;
    45  2. Nash aborts at first unbound variable;
    46  3. It's possible to check the result status of every component of a pipe;
    47  4. **no eval**;
    48  5. Strings are pure strings (no evaluation of variables);
    49  6. No wildcards (globbing) of files; ('rm \*' removes a file called '\*');
    50      - On windows, the terminal does the globbing when in interactive mode.
    51      - On unix there's libs/completions to achieve something similar.
    52  7. No [obscure](http://explainshell.com/) syntax;
    53  8. Support tooling for indent/format and statically analyze the scripts;
    54  
    55  ## Installation
    56  
    57  Nash uses two environment variables: **NASHROOT** to find the standard nash library and **NASHPATH** to find libraries in general (like user's code).
    58  
    59  It is important to have two different paths since this will allow you
    60  to upgrade nash (overwrite nash stdlib) without risking lost your code.
    61  
    62  If **NASHPATH** is not set, a default of $HOME/nash will be assumed 
    63  ($HOMEPATH/nash on windows).
    64  If **NASHROOT** is not set, a default of $HOME/nashroot will be assumed 
    65  ($HOMEPATH/nashroot on windows).
    66  
    67  The libraries lookup dir will be $NASHPATH/lib.
    68  The standard library lookup dir will be $NASHROOT/stdlib.
    69  
    70  After installing the nash binary will be located at $NASHROOT/bin.
    71  
    72  ### Installing a Release
    73  
    74  Installing is so stupid that we provide small scripts to do it.
    75  If your platform is not supported take a look at the existent ones
    76  and send a MR with the script for your platform.
    77  
    78  #### Unix
    79  
    80  If you run a unix based machine (Linux, Darwin/OSX, *BSD, etc)
    81  you can use the script below:
    82  
    83  Run:
    84  
    85  ```
    86  ./hack/install/unix.sh
    87  ```
    88  
    89  ### Master
    90  
    91  Run:
    92  
    93  ```
    94  make install
    95  ```
    96  
    97  ## Getting started
    98  
    99  Nash syntax resembles a common shell:
   100  
   101  ```
   102  nash
   103  λ> echo "hello world"
   104  hello world
   105  ```
   106  Pipes works like borne shell and derivations:
   107  
   108  ```sh
   109  λ> cat spec.ebnf | wc -l
   110  108
   111  ```
   112  Output redirection works like Plan9 rc, but not only for filenames. It
   113  supports output redirection to tcp, udp and unix network protocols 
   114  (unix sockets are not supported on windows).
   115  
   116  ```sh
   117  # stdout to log.out, stderr to log.err
   118  λ> ./daemon >[1] log.out >[2] log.err
   119  # stderr pointing to stdout
   120  λ> ./daemon-logall >[2=1]
   121  # stdout to /dev/null
   122  λ> ./daemon-quiet >[1=]
   123  # stdout and stderr to tcp address
   124  λ> ./daemon >[1] "udp://syslog:6666" >[2=1]
   125  # stdout to unix file
   126  λ> ./daemon >[1] "unix:///tmp/syslog.sock"
   127  ```
   128  
   129  **For safety, there's no `eval` or `string/tilde expansion` or `command substitution` in Nash.**
   130  
   131  To assign command output to a variable exists the '<=' operator. See the example
   132  below:
   133  ```sh
   134  var fullpath <= realpath $path | xargs -n echo
   135  echo $fullpath
   136  ```
   137  The symbol '<=' redirects the stdout of the command or function invocation in the
   138  right-hand side to the variable name specified.
   139  
   140  If you want the command output splited into an array, then you'll need
   141  to store it in a temporary variable and then use the builtin `split` function.
   142  
   143  ```sh
   144  var out <= find .
   145  var files <= split($out, "\n")
   146  
   147  for f in $files {
   148          echo "File: " + $f
   149  }
   150  ```
   151  
   152  To avoid problems with spaces in variables being passed as
   153  multiple arguments to commands, nash pass the contents of each
   154  variable as a single argument to the command. It works like
   155  enclosing every variable with quotes before executing the command.
   156  Then the following example do the right thing:
   157  
   158  ```sh
   159  var fullname = "John Nash"
   160  ./ci-register --name $fullname --option somevalue
   161  ```
   162  On bash you need to enclose the `$fullname` variable in quotes to avoid problems.
   163  
   164  Nash syntax does not support shell expansion from strings. There's no way to
   165  do things like the following in nash:
   166  
   167  ```bash
   168  echo "The date is: $(date +%D)" # DOESNT WORKS!
   169  ```
   170  
   171  Instead you need to assign each command output to a proper variable and then
   172  concat it with another string when needed (see the [reference docs](./docs/reference.md)).
   173  
   174  In the same way, nash doesn't support shell expansion at `if` condition.
   175  For check if a directory exists you must use:
   176  ```sh
   177  -test -d $rootfsDir    # if you forget '-', the script will be aborted here
   178                         # if path not exists
   179  
   180  if $status != "0" {
   181          echo "RootFS does not exists."
   182          exit $status
   183  }
   184  ```
   185  Nash stops executing the script at first error found and, in the majority of times, it is what
   186  you want (specially for deploys). But Commands have an explicitly
   187  way to bypass such restriction by prepending a dash '-' to the command statement.
   188  For example:
   189  
   190  ```sh
   191  fn cleanup()
   192          -rm -rf $buildDir
   193          -rm -rf $tmpDir
   194  }
   195  ```
   196  
   197  The dash '-' works only for operating system commands, other kind of errors are impossible to bypass.
   198  For example, trying to evaluate an unbound variable aborts the program with error.
   199  
   200  ```sh
   201  λ> echo $PATH
   202  /bin:/sbin:/usr/bin:/usr/local/bin:/home/user/.local/bin:/home/user/bin:/home/user/.gvm/pkgsets/go1.5.3/global/bin:/home/user/projects/3rdparty/plan9port/bin:/home/user/.gvm/gos/go1.5.3/bin
   203  λ> echo $bleh
   204  ERROR: Variable '$bleh' not set
   205  ```
   206  
   207  Long commands can be split in multiple lines:
   208  
   209  ```sh
   210  λ> (aws ec2 attach-internet-gateway	--internet-gateway-id $igwid
   211  									--vpc-id $vpcid)
   212  
   213  λ> var instanceId <= (
   214  	aws ec2 run-instances
   215  			--image-id ami-xxxxxxxx
   216  			--count 1
   217  			--instance-type t1.micro
   218  			--key-name MyKeyPair
   219  			--security-groups my-sg
   220      | jq ".Instances[0].InstanceId"
   221  )
   222  λ> echo $instanceId
   223  ```
   224  
   225  # Accessing command line args
   226  
   227  When you run a nash script like:
   228  
   229  ```
   230  λ> nash ./examples/args.sh --arg value
   231  ```
   232  
   233  You can get the args using the **ARGS** variable, that is a list:
   234  
   235  ```
   236  #!/usr/bin/env nash
   237  
   238  echo "iterating through the arguments list"
   239  echo ""
   240  for arg in $ARGS {
   241  	echo $arg
   242  }
   243  ```
   244  
   245  
   246  # Namespace features
   247  
   248  Nash is built with namespace support only on Linux (Plan9 soon). If
   249  you use OSX, BSD or Windows, then the `rfork` keyword will fail.
   250  
   251  *The examples below assume you are on a Linux box.*
   252  
   253  Below are some facilities for namespace management inside nash.
   254  Make sure you have USER namespaces enabled in your kernel:
   255  
   256  ```sh
   257  zgrep CONFIG_USER_NS /proc/config.gz
   258  CONFIG_USER_NS=y
   259  ```
   260  
   261  If it's not enabled you will need root privileges to execute every example below...
   262  
   263  Creating a new process in a new USER namespace (u):
   264  
   265  ```sh
   266  λ> id
   267  uid=1000(user) gid=1000(user) groups=1000(user),98(qubes)
   268  λ> rfork u {
   269       id
   270  }
   271  uid=0(root) gid=0(root) groups=0(root),65534
   272  ```
   273  Yes, Linux supports creation of containers by unprivileged users. Tell
   274  this to the customer success of your container-infrastructure-vendor. :-)
   275  
   276  The default UID mapping is: Current UID (getuid) => 0 (no
   277  range support). I'll look into more options for this in the future.
   278  
   279  Yes, you can create multiple nested user namespaces. But kernel limits
   280  the number of nested user namespace clones to 32.
   281  
   282  ```sh
   283  λ> rfork u {
   284      echo "inside first container"
   285  
   286      id
   287  
   288      rfork u {
   289          echo "inside second namespace..."
   290  
   291          id
   292      }
   293  }
   294  ```
   295  
   296  You can verify that other types of namespace still requires root
   297  capabilities, see for PID namespaces (p).
   298  
   299  ```sh
   300  λ> rfork p {
   301      id
   302  }
   303  ERROR: fork/exec ./nash: operation not permitted
   304  ```
   305  
   306  The same happens for mount (m), ipc (i) and uts (s) if used without
   307  user namespace (u) flag.
   308  
   309  The `c` flag stands for "container" and is an alias for upmnis (all
   310  types of namespaces).  If you want another shell (maybe bash) inside
   311  the namespace:
   312  
   313  ```sh
   314  λ> rfork c {
   315      bash
   316  }
   317  [root@stay-away nash]# id
   318  uid=0(root) gid=0(root) groups=0(root),65534
   319  [root@stay-away nash]# mount -t proc proc /proc
   320  [root@stay-away nash]#
   321  [root@stay-away nash]# ps aux
   322  USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
   323  root         1  0.0  0.0  34648  2748 pts/4    Sl   17:32   0:00 -rcd- -addr /tmp/nash.qNQa.sock
   324  root         5  0.0  0.0  16028  3840 pts/4    S    17:32   0:00 /usr/bin/bash
   325  root        23  0.0  0.0  34436  3056 pts/4    R+   17:34   0:00 ps aux
   326  ```
   327  
   328  Everything except the `rfork` is like a common shell. Rfork will spawn a
   329  new process with the namespace flags and executes the commands inside
   330  the block on this namespace. It has the form:
   331  
   332  ```sh
   333  rfork <flags> {
   334      <statements to run inside the container>
   335  }
   336  ```
   337  
   338  # OK, but how scripts should look like?
   339  
   340  See the project [nash-app-example](https://github.com/madlambda/nash-app-example).
   341  
   342  # Didn't work?
   343  
   344  I've tested in the following environments:
   345  
   346      Linux 4.7-rc7
   347      Archlinux
   348  
   349      Linux 4.5.5 (amd64)
   350      Archlinux
   351  
   352      Linux 4.3.3 (amd64)
   353      Archlinux
   354  
   355      Linux 4.1.13 (amd64)
   356      Fedora release 23
   357  
   358      Linux 4.1.13 (amd64)
   359      Debian 8
   360  
   361  # Language specification
   362  
   363  The specification isn't complete yet, but can be found
   364  [here](https://github.com/madlambda/nash/blob/master/spec.ebnf).
   365  The file `spec_test.go` makes sure it is sane.
   366  
   367  # Some Bash comparisons
   368  
   369  | Bash | Nash | Description	|
   370  | --- | --- | --- |
   371  | `GOPATH=/home/user/gopath` | `GOPATH="/home/user/gopath"` | Nash enforces quoted strings |
   372  | `GOPATH="$HOME/gopath"` | `GOPATH=$HOME+"/gopath"` | Nash doesn't do string expansion |
   373  | `export PATH=/usr/bin` | `PATH="/usr/bin"`<br>`setenv PATH` | setenv operates only on valid variables |
   374  | `export` | `showenv` | |
   375  | `ls -la` | `ls -la` | Simple commads are identical |
   376  | `ls -la "$GOPATH"` | `ls -la $GOPATH` | Nash variables shouldn't be enclosed in quotes, because it's default behaviour |
   377  | `./worker 2>log.err 1>log.out` | `./worker >[2] log.err >[1] log.out` | Nash redirection works like plan9 rc |
   378  | `./worker 2>&1` | `./worker >[2=1]` | Redirection map only works for standard file descriptors (0,1,2) |
   379  
   380  # Security
   381  
   382  The PID 1 of every namespace created by `nash` is the same nash binary reading
   383  commands from the parent shell via unix socket. It allows the parent namespace
   384  (the script that creates the namespace) to issue commands inside the child
   385  namespace. In the current implementation the unix socket communication is not
   386  secure yet.
   387  
   388  # Installing libraries
   389  
   390  Lets say you have a nash library and you want to install it. For example you have
   391  the following:
   392  
   393  ```
   394  awesome/code.sh
   395  ```
   396  
   397  And you want to install it so you can write code like this:
   398  
   399  ```
   400  import awesome/code
   401  
   402  code_do_awesome_stuff()
   403  ```
   404  
   405  All you have to do is run:
   406  
   407  ```
   408  nash -install ./awesome
   409  ```
   410  
   411  Or:
   412  
   413  ```
   414  nash -install /absolute/path/awesome
   415  ```
   416  
   417  The entire awesome dir (and its subdirs) will be copied where nash
   418  searches for libraries (dependent on environment variables).
   419  
   420  This is the recommended way of installing nash libraries (althought
   421  you can do it manually if you want).
   422  
   423  Single files can also be installed as packages, for example:
   424  
   425  ```
   426  nash -install ./awesome/code.sh
   427  ```
   428  
   429  Will enable you to import like this:
   430  
   431  ```
   432  import code
   433  ```
   434  
   435  If there is already a package with the given name it will be
   436  overwritten.
   437  
   438  
   439  # Releasing
   440  
   441  To generate a release basically:
   442  
   443  * Generate the release on github
   444  * Clone the generated tag
   445  * Run: ``` make release "version=<version>" ```
   446  
   447  Where **<version>** must match the version of the git tag.
   448  
   449  # Want to contribute?
   450  
   451  Open issues and PR :)
   452  The project is in an early stage, be patient because things can change in the future.
   453  
   454  > "What I cannot create, I do not understand."
   455  >
   456  > -- Richard Feynman