Coffee Space 
Typically most shells on the Linux operating system will run Bash
(/bin/bash), Sh (/bin/sh - typically an alias
to Bash), Ash, Zsh (more popular now) and others. Whilst these are cool
and interesting, these are typically meant for really high-end, high-RAM
systems.
Recently I have been having trouble with my 128MB server, trying to run too much
on such a low-end system. One of the things I am having issue with is
/bin/bash consuming a significant amount of RAM, once you
have 10 shells open or so. The other repetitive RAM consuming resource
was screen, but I don’t have the time or expertise to
replace this.
This got me thinking: “If I were to design a new shell for this use-case, what may it look like?”.
This is a list of things it must have:
Of course, this should not be compiled…
And this is a list of things that would be nice to have:
Generally I would want to use the Elk JS engine (and my review of it). It deals with all of the issues I would want to address and it is ultra simple.
I would probably look to expose the C library functions, so for
comparing strings you may do something like the following for strcmp():
0001 let a = "hello";
0002 let b = "world";
0003 let c = "hello";
0004 if(strcmp(a, b) !== 0){
0005 /* a != b */
0006 }
0007 if(strcmp(b, c) !== 0){
0008 /* b != c */
0009 }
0010 if(strcmp(c, a) === 0){
0011 /* c == a */
0012 }
For running external programs, we could then make use of exec()
to run external programs:
0013 execvp("ls");
It would take some thinking to figure out a nice way of returning the
exit condition of the comamnd, the stdout and
stdin. Ideally we want some kind of support for pipes, but
it is not clear to be what may be the cleanest way to support this. Elk
supports objects, so perhaps there is some Pipe type where
we can define the stdin, stdout and
stderr. This may look like:
0014 let pipe = /* Some declaration */; 0015 pipe.stdin = other_pipe.stdout; 0016 execvp(pipe, "ls"); 0017 /* Do some stuff */ 0018 printf(pipe.stdout); 0019 printf(pipe.stderr);
It is not very clean and maybe there is a nicer way to achieve this. Perhaps another option could be:
0020 pipe(funct1(), func2(), /*..*/);
We would probably want to implement our own cd (change
directory) function so that we can track the current location the
programs are to be run in. For simplicity the context would probably be
global.
Edit: It occurs to me that there is actually a nicer
way to implement this, where we simply allow for the dropping of the
function rounded-brackets (( and )). We can
also use a symbol to replace the pipe name, such as
|, and get to something like:
0021 | funct1() funct2(); 0022 // Equiv: pipe(funct1(), funct2()); 0023 | prog1 prog2; 0024 // Equiv: pipe(prog1(), prog2()); 0025 | prog1 (| funct1, funct2(/* params */)); 0026 // Equiv: pipe(prog1, pipe(funct1(), funct2(/* params */))); 0027 | prog1 funct1 funct2(/* params */)); 0028 // Equiv: pipe(prog1, funct1(), funct2(/* params */));
Essentially the rounded-brackets () become optional. I
also quite like the simplicity of using & for forking,
perhaps it would also follow the same syntax:
0029 fork(prog1()); 0030 &(prog1()); 0031 & prog1(); 0032 & prog1;
Obviously the brackets would be the cleaner syntax in this case, especially if you want arguments for programs or functions.
In terms of a name, jsh appears
taken for a Java based shell idea. A Jay is a bird, so maybe
there could be something funny here regarding a Jay bird with a shell,
like a flying tortoise. So “JayShell” could be good.
If I get some time I will consider hacking together a quick implementation of this. Even something that is ultra-lightweight that can execute commands is already really useful. If we can process their output is some meaningful way, this is also really cool.