logo

drewdevault.com

[mirror] blog and personal website of Drew DeVault git clone https://hacktivis.me/git/mirror/drewdevault.com.git

2023-04-18-A-new-shell-for-Unix.md (5146B)


  1. ---
  2. title: "rc: a new shell for Unix"
  3. date: 2023-04-18
  4. ---
  5. [rc] is a Unix shell I've been working on over the past couple of weeks, though
  6. it's been in the design stages for a while longer than that. It's not done or
  7. ready for general use yet, but it is interesting, so let's talk about it.
  8. [rc]: https://git.sr.ht/~sircmpwn/rc
  9. As the name (which is subject to change) implies, rc is inspired by the Plan 9
  10. [rc][plan9] shell. It's not an implementation of Plan 9 rc, however: it departs
  11. in many notable ways. I'll assume most readers are more familiar with POSIX
  12. shell or Bash and skip many of the direct comparisons to Plan 9. Also, though
  13. most of the features work as described, the shell is a work-in-progress and some
  14. of the design I'm going over today has not been implemented yet.
  15. [plan9]: http://man.9front.org/1/rc
  16. Let's start with the basics. Simple usage works much as you'd expect:
  17. ```
  18. name=ddevault
  19. echo Hello $name
  20. ```
  21. But there's already something important that might catch your eye here: the lack
  22. of quotes around $name. One substantial improvement rc makes over POSIX shells
  23. and Bash right off the bat is fixing our global shell quoting nightmare. There's
  24. no need to quote variables!
  25. ```
  26. # POSIX shell
  27. x="hello world"
  28. printf '%s\n' $x
  29. # hello
  30. # world
  31. # rc
  32. x="hello world"
  33. printf '%s\n' $x
  34. # hello world
  35. ```
  36. Of course, the POSIX behavior is actually useful sometimes. rc provides for this
  37. by acknowledging that shells have not just one fundamental type (strings), but
  38. two: strings and *lists* of strings, i.e. argument vectors.
  39. ```
  40. x=(one two three)
  41. echo $x(1) # prints first item ("one")
  42. echo $x # expands to arguments (echo "one" "two" "three")
  43. echo $#x # length operator: prints 3
  44. x="echo hello world"
  45. $x
  46. # echo hello world: command not found
  47. x=(echo hello world)
  48. $x
  49. # hello world
  50. # expands to a string, list values separated with space:
  51. $"x
  52. # echo hello world: command not found
  53. ```
  54. You can also slice up lists and get a subset of items:
  55. ```
  56. x=(one two three four five)
  57. echo $x(-4) # one two three four
  58. echo $x(2-) # two three four five
  59. echo $x(2-4) # two three four
  60. ```
  61. A departure from Plan 9 rc is that the list operators can be used with strings
  62. for string operations as well:
  63. ```
  64. x="hello world"
  65. echo $#x # 11
  66. echo $x(2) # e
  67. echo $x(1-5) # hello
  68. ```
  69. rc also supports loops. The simple case is iterating over the command line
  70. arguments:
  71. ```
  72. % cat test.rc
  73. for (arg) {
  74. echo $arg
  75. }
  76. % rc test.rc one two three
  77. one
  78. two
  79. three
  80. ```
  81. { } is a command like any other; this can be simplified to for (arg) echo
  82. $arg. You can also enumerate any list with in:
  83. ```
  84. list=(one two three)
  85. for (item in $list) {
  86. echo $item
  87. }
  88. ```
  89. We also have while loops and if:
  90. ```
  91. while (true) {
  92. if (test $x -eq 10) {
  93. echo ten
  94. } else {
  95. echo $x
  96. }
  97. }
  98. ```
  99. Functions are defined like so:
  100. ```
  101. fn greet {
  102. echo Hello $1
  103. }
  104. greet ddevault
  105. ```
  106. Again, any command can be used, so this can be simplified to fn greet echo $1.
  107. You can also add named parameters:
  108. ```
  109. fn greet(user time) {
  110. echo Hello $user
  111. echo It is $time
  112. }
  113. greet ddevault `{date}
  114. ```
  115. Note the use of `{script...} instead of $() for command expansion. Additional
  116. arguments are still placed in $*, allowing for the user to combine
  117. variadic-style functions with named arguments.
  118. Here's a more complex script that I run to perform sanity checks before applying
  119. patches:
  120. ```
  121. #!/bin/rc
  122. fn check_branch(branch) {
  123. if (test `{git rev-parse --abbrev-ref HEAD} != $branch) {
  124. echo "Error: not on master branch"
  125. exit 1
  126. }
  127. }
  128. fn check_uncommitted {
  129. if (test `{git status -suno | wc -l} -ne 0) {
  130. echo "Error: you have uncommitted changes"
  131. exit 1
  132. }
  133. }
  134. fn check_behind {
  135. if (test `{git rev-list "@{u}.." | wc -l} -ne 0) {
  136. echo "Error: your branch is behind upstream"
  137. exit 1
  138. }
  139. }
  140. check_branch master
  141. check_uncommitted
  142. check_behind
  143. exec git pull
  144. ```
  145. That's a brief introduction to rc! Presently it clocks in at about 2500 lines of
  146. Hare. It's not done yet, so don't get too excited, but much of what's described
  147. here is already working. Some other stuff which works but I didn't mention
  148. include:
  149. - Boolean compound commands (x && y, x || y)
  150. - Pipelines, which can pipe arbitrary file descriptors ("x |[2] y")
  151. - Redirects, also including arbitrary fds ("x >[2=1] file")
  152. It also has a [formal context-free grammar][grammar], which is a
  153. work-in-progress but speaks to our desire to have a robust description of the
  154. shell available for users and other implementations. We use Ember Sawady's
  155. excellent [madeline][made] for our interactive mode, which supports command line
  156. editing, history, ^r, and fish-style forward completion OOTB.
  157. [grammar]: https://git.sr.ht/~sircmpwn/rc/tree/master/item/doc/grammar.txt
  158. [made]: https://git.d2evs.net/~ecs/madeline/
  159. Future plans include:
  160. - Simple arithmetic expansion
  161. - Named pipe expansions
  162. - Sub-shells
  163. - switch statements
  164. - Port to [ares](https://ares-os.org)
  165. - Find a new name, perhaps
  166. It needs a small amount of polish, cleanup, and bugs fixed as well.
  167. I hope you find it interesting! I will let you know when it's done. Feel free
  168. to [play with it][rc] in the meanwhile, and maybe send some patches?