github.com/xiaq/elvish@v0.12.0/website/src/learn/fundamentals.md (about) 1 <!-- toc --> 2 3 **This tutorial is quite incomplete, and it is being constantly expanded.** 4 5 This tutorial introduces the fundamentals of shell programming with Elvish. 6 It does not assume familiarity with other shells, but some understanding of 7 basic programming concepts is required. 8 9 This tutorial evolves around a "hello" program. But if your primary interest 10 in shell programming is not saying hello (which, by the way, is a shame), the 11 tutorial also contains some hints and examples on how to apply to knowledge to 12 non-hello applications. 13 14 **Note**: Elvish is very similar to other shell languages in many aspects, but 15 also very different in others. When transferring your knowledge of Elvish to 16 another shell, it is worthwhile to first check how things work there. 17 18 19 # Hello, world! 20 21 Let's begin with the most traditional "hello world" program. In Elvish, 22 you invoke the `echo` **command** to print something on the terminal: 23 24 ```elvish-transcript 25 ~> echo "Hello, world!" 26 Hello, world! 27 ``` 28 29 In Elvish, as in other shells, command invocations follow a simple structure: you 30 write the command name, followed by arguments, all separated by spaces (or 31 tabs). No parentheses or commas are needed. 32 33 We enclose our text here in double quotes, making it a **string literal**. 34 Compared to other languages, shell languages are a bit sloppy in that they 35 allow you to write strings *without* quotes. The following also works: 36 37 ```elvish-transcript 38 ~> echo Hello, world! 39 Hello, world! 40 ``` 41 42 However, the way it works has a subtle difference: here `Hello,` and `world!` 43 are two arguments (remember that spaces separate arguments), and `echo` joins 44 them together with a space. This is apparent if you put multiple spaces 45 between them: 46 47 ```elvish-transcript 48 ~> echo Hello, world! 49 Hello, world! 50 ~> echo "Hello, world!" 51 Hello, world! 52 ``` 53 54 When you write your message without quotes, no matter how many spaces there 55 are, it is always the same two arguments `Hello,` and `world!`. If you quote 56 your message, the spaces are part of the string and thus preserved. 57 58 It is a good idea to always quote your string when it contains spaces or any 59 special symbols other than period (`.`), dash (`-`) or underscore (`_`). 60 61 62 ## It Doesn't Have to be Hello 63 64 All command invocations in shell have the same basic structure: name of 65 command, followed by arguments. Elvish provides a lot of useful [builtin 66 commands](/ref/builtin.html), and `echo` is just one of them. As another 67 example, there is one for generating random numbers, which you can use as a 68 digital dice: 69 70 ```elvish-transcript 71 ~> # randint a b generates an integer from range a...b-1 72 randint 1 7 73 ▶ 3 74 ``` 75 76 Arithmetic operations are also commands. Since they also follow the same order 77 of command name first, the syntax deviates a bit from usual mathematical 78 notations: 79 80 ```elvish-transcript 81 ~> * 17 28 # multiplication 82 ▶ 476 83 ~> ^ 2 10 # exponention 84 ▶ 1024 85 ``` 86 87 The commands introduced above -- `echo`, `randint`, `*` and `^` -- are all 88 **builtin** commands, commands that Elvish provides for you. 89 90 As a shell language, however, Elvish also makes it trivial to use **external** 91 commands, commands implemented as separate programs. Chances are you have 92 already used some of them like `ls` or `cat`. Here we show you how to obtain 93 Elvish entirely from the command line: you can use `wget` to download files, 94 `shasum` to verify its checksum, and `tar` to uncompress them, all of which 95 are external commands: 96 97 ```elvish-transcript 98 ~> wget https://dl.elv.sh/elvish-linux-amd64-HEAD.tar.gz 99 ... omit ... 100 elvish-linux.tar.gz 100%[======================>] 4.91M 10.9MB/s in 0.4s 101 ~> shasum -a 256 elvish-linux.tar.gz 102 0fc3c145a81345a1c49576b86ef12156d4eba1829e1bb20e9c39d115991a9c7b elvish-linux.tar.gz 103 ~> tar xvf elvish-linux.tar.gz 104 x elvish 105 ``` 106 107 With the most basic knowledge of how to invoke commands, there is already a 108 myriad of functionalities at your fingertip. 109 110 111 # Hello, {insert user name}! 112 113 The "hello world" program is a classic, but the fact that it always prints the 114 same simple message does make it a little bit boring. 115 116 One way to make programs more useful is to teach them to do different things 117 depending on the context. In the case of our "hello world" program, why not 118 teach it to greet whoever is running the program? 119 120 ```elvish-transcript 121 ~> echo "Hello, world! Hello, "$E:USER"!" 122 Hello, world! Hello, xiaq! 123 ``` 124 125 There are several things happening here. First, `$E:USER` represents the `USER` 126 **environment variable** ("E" being mnemonic for "environment"). In UNIX 127 environments, it is usually set to the name of the current user. Second, we 128 are running several strings and a variable all together: in this case, Elvish 129 will concatenate them for you. Hence the result we see. 130 131 Depending on your taste, you might feel that it's nicer to greet the world and 132 the user on separate lines. To do this, we can insert `\n`, an **escape 133 sequence** representing a newline in our string: 134 135 ```elvish-transcript 136 ~> echo "Hello, world!\nHello, "$E:USER"!" 137 Hello, world! 138 Hello, xiaq! 139 ``` 140 141 There are many such sequences starting with a backslash, including `\\` which 142 represents the backslash itself. Beware that such escape sequences only work 143 within double quotes. 144 145 Environment variables are not the only way to learn about a computer system; 146 we can also gain more information by invoking commands. For instance, the 147 `uname` command tells you which operation system the computer is running: 148 149 ```elvish-transcript 150 ~> uname 151 Darwin 152 ``` 153 154 ([Darwin](https://en.wikipedia.org/wiki/Darwin_(operating_system)), by the 155 way, is the open-source core of macOS and iOS.) 156 157 To incorporate the output of `uname` into our hello message, we can first 158 **capture** its output using parentheses and keep it in a variable using the 159 assignment form `variable = value`: 160 161 ```elvish-transcript 162 ~> os = (uname) 163 ~> # Variable "os" now contains "Darwin" 164 ``` 165 166 We can then use this variable, in a similar way to how we used the environment 167 variable, just without the `E:` namespace: 168 169 ```elvish-transcript 170 ~> echo "Hello, "$os" user!" 171 Hello, Darwin user! 172 ``` 173 174 I used a variable for demonstration, but it's also possible to forego the 175 variable and use the captured output directly: 176 177 ```elvish-transcript 178 ~> echo "Hello, "(uname)" user!" 179 Hello, Darwin user! 180 ``` 181 182 ## It Doesn't Have to be Hello 183 184 Output captures can get you quite far in combining commands. For instance, you 185 can use output captures to construct do complex arithmetic involving more than 186 one operation: 187 188 ```elvish-transcript 189 ~> # compute the answer to life, universe and everything 190 * (+ 3 4) (- 100 94) 191 ▶ 42 192 ``` 193 194 195 <!-- 196 # Hello, everyone! 197 198 199 Now let's say you want to say hello to several people, and typing `Hello` repeatedly is tiresome. You can save some work by using a **for-loop**: 200 201 ```elvish 202 for name [Julius Pompey Marcus] { 203 echo 'Hello, '$name'!' 204 } 205 ``` 206 207 In elvish you can put newlines between the elements to loop over, as long as they are terminated by `; do`. 208 209 For easier reuse, you can also create a **list** to store the names: 210 211 ```elvish 212 triumvirate = [Julius Pompey Marcus] 213 ``` 214 215 Lists are surrounded by square brackets, like in several other languages. Elements are separated by whitespaces. 216 217 As you may have noticed, dashes are allowed in variable names. You are encouraged to use them instead of underscores; they are easier to type and more readable (after a little getting-used-to). 218 219 Now it's time to use our list of the first triumvirate: 220 221 ```elvish 222 for name in $first-triumvirate; do 223 echo 'Hello, '$name'!' 224 done 225 ``` 226 227 This will, however, results in an error, saying that a string and a list cannot be concatenated. Why? Remember that `$x` is always one value. This is even true for lists, so the `for` loop only sees one value to loop over, namely the list itself. 228 229 To make multiple words out of a list, you must explicitly **splice** the list with an `@` before the variable name: 230 231 ```elvish 232 for name in $@first-triumvirate; do 233 echo 'Hello, '$name'!' 234 done 235 ``` 236 237 # Each person gets $hello~'ed 238 239 The for-loop we just show can also be written in a functional style: 240 241 ```elvish 242 each [name]{ 243 echo 'Hello, '$name'!' 244 } $first-triumvirate 245 ``` 246 247 This looks similar to the for-loop version, but it makes use of a remarkable construct -- an **anonymous function**, also known as a **lambda**. In elvish, a lambda is syntactically formed by an argument list followed immediately (without space) by a function body enclosed in braces. Here, `[name]{ echo 'Hello, '$name'!' }` is a lambda that takes exactly one argument and calls `echo` to do the helloing. We pass it along a list to the `each` builtin, which runs the function on each element of the list. 248 249 Functions, like strings and lists, can be stored in variables: 250 251 ```elvish 252 hello=[name]{ echo 'Hello, '$name'!' } 253 each $hello $first-triumvirate 254 ``` 255 256 To call a function, simply use it as a command: 257 258 ```elvish 259 $hello 'Mark Antony' # Hello, Mark Anthony! 260 ``` 261 262 You must have noticed that you have to use `$hello` instead of `hello` to call the function. This is because the *hello-the-variable* and *hello-the-command* are different enitites. To define new commands, use the `fn` special form: 263 264 ```elvish 265 fn hello [name]{ 266 echo 'Hello, '$name'!' 267 } 268 hello Cicero # Hello, Cicero! 269 ``` 270 271 Users of traditional shells and Common Lisp will find this separation of the variable namespace and command namespace familiar. 272 273 However, in elvish this separation is only superficial; what `fn hello` really does is just defining a variable called `hello~`. You can prove this: 274 275 ```elvish 276 echo $hello~ # <closure ...> 277 $hello~ Brutus # Hello, Brutus! 278 each $hello~ $first-triumvirate # (Hello to the first triumvirate) 279 ``` 280 281 Conversely, defining a variable `hello~` will also create a command named `hello`: 282 283 ```elvish 284 hello~ = [name]{ echo "Hello, hello, "$name"!" } 285 hello Augustus # Hello, Augustus! 286 ``` 287 288 <!-- 289 ``` 290 What I want to get into this document: 291 292 [ ] Command substitution 293 294 [ ] Rich pipeline 295 296 [X] Lists 297 298 [ ] Maps 299 300 [X] Lambdas 301 302 [X] fn 303 304 [X] $& 305 306 [X] One variable, one argument 307 308 [X] String syntax 309 310 [X] Lack of interpolation 311 312 [X] Several builtins -- each println 313 314 [ ] Editor API 315 316 [ ] Exception and verdict 317 318 [X] E: namespace for environment variables 319 320 [ ] e: namespace for external commands 321 322 [ ] Modules 323 324 Write for readers with a moderate knowledge of a POSIXy shell (bash, zsh, ...) 325 --> 326