github.com/NeowayLabs/nash@v0.2.2-0.20200127205349-a227041ffd50/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