This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
ElvishJerricco's profile picture

ClamShell - Advanced Shell With a Shell Language and Bash-like Piping

Started by ElvishJerricco, 13 October 2014 - 01:35 AM
ElvishJerricco #1
Posted 13 October 2014 - 03:35 AM
ClamShell


ClamShell is an advanced shell for ComputerCraft that adds pipelining, a shell scripting language, and several tools to make use of these features.

Pipelining

Pipelining allows programs to be made small and modular. Programs with their output piped won't print to the terminal. Instead, they'll print to whatever they're being piped to. If that's another program, then that program will get the first program's output from its read() calls instead of the normal keyboard input.

Let's look at two programs, ls and glep. Glep is ClamShell's equivalent to Unix's grep, which is a program that filters the lines of a file or its standard input. The major difference is that glep uses Lua patterns instead of regex. And ls of course just lists the current (or specified) directory.


ls | glep sh

This command will pipe ls into glep, whose argument tells it to filter out lines that don't have "sh" in them somewhere. The output of this command will be all the files in the current directory that have "sh" in their names. (Also worth noting, ClamShell reimplements ls so that it outputs in lines instead of the pretty formatting, but only when it's being piped into something else).

You can also pipe into files.

ls | glep sh > out.txt

or you can append to out.txt instead of overwriting

ls | glep sh >> out.txt

It works by running programs in parallel, and creating stdin, stdout, and stderr file handles for each program. The program at the beginning of the pipe has the normal read function for its stdin.readLine. The program at the end of the pipe gets the regular write function for stdout.write. The programs in between have their stdouts and stdins linked together. Before each program's coroutine is resumed, _G.write and _G.read are rerouted to the appropriate stdout and stdin functions.

ClamShell's Scripting Language

ClamShell doesn't parse commands the same way as the CraftOS shell. CraftOS's shell looks for a program name followed by arguments and that's it. ClamShell uses a scripting language with some features that CraftOS's shell can't manage.

ClamShell scripts can be written inline, but you'll need semi-colons after each command if you want to do more than one. Or you can save scripts in .sh files, which ClamShell can run.


DIR = "someDir"
if test -d $DIR {
	echo "$DIR is a directory!"
}

Two features are demonstrated here.

DIR = "someDir"
Here we see a shell environment variable being set. Whenever $DIR is encountered, it will be replaced with the string "someDir".

if test -d $DIR {
An if statement in ClamShell runs a command and sees if it errors. If it exits cleanly, the if block executes, else it is skipped.

Files written in ClamShell's language can be run from the command line just like any other, although it is recommended that you run them under a child shell via

sh my_script.sh
since they can declare and modify environment variables.

Tools

ClamShell includes several command line tools that make use of ClamShell's features.
  • cat {files}
  • Cat will print the contents of one or more files. Or, if no file is passed, it will repeat the standard input.
  • echo {arguments}
  • Echo will print the arguments with a space between them.
  • glep (pattern) {files}
  • Glep will read from either the standard input, or a list of files, and print only the lines that match with the Lua pattern.
  • test (-d|-e|-f) (file)
  • Test takes two arguments. -d, -e, and -f indicate a test for directory, existence, or file respectively. If the given file is described by the argument specified, test will exit cleanly. Else, it will error.
  • xargs (command) {arguments}
  • xargs is used to take lines from the standard input and use them as arguments for a program.
Planned Features
  • More control flow (while, for, haven't gotten else yet)
  • Using command outputs in expressions like this: echo $(echo hey)
Suggestions are very welcome.

GitHub and Installation

GitHub
Install: grin-get install Team-CC-Corp/ClamShell
Edited on 02 April 2015 - 11:02 PM
AssossaGPB #2
Posted 13 October 2014 - 04:02 AM
Sweet! Looks epic. I'm gonna try using tomorrow.
InputUsername #3
Posted 15 October 2014 - 10:26 PM
This is really cool! I've been wanting to make something like this for a while now, but couldn't think of how to do it. Looks like you succeeded, it works well. Good job.

Looking forward to the upcoming features too. I like the idea of bringing UNIX to ComputerCraft.
ElvishJerricco #4
Posted 19 October 2014 - 05:49 AM
Update. Changes:
  • Else and else if statements
  • While statements
Phoenix323 #5
Posted 27 January 2015 - 10:23 PM
Awesome Here Is What I Made

DIR = "booting"
if test -d $DIR {
echo "$DIR is a directory!"
}

FILE = "t.sh"

if test -f $FILE {
echo "$FILE is a file!"
echo "Print the contents of $FILE"
cat $FILE
}
Phoenix323 #6
Posted 28 January 2015 - 01:40 AM
What is a lua pattern
ElvishJerricco #7
Posted 28 January 2015 - 05:50 AM
What is a lua pattern

You'll want to read this.
ElvishJerricco #8
Posted 03 April 2015 - 01:08 AM
Updated: Migrated to new ownership at Team C^3
SquidDev #9
Posted 03 April 2015 - 08:18 AM
Updated: Migrated to new ownership at Team C^3

You didn't mention any of the new features :(/>
  • History and current directory are saved between sessions
  • Scrollback on terminal history
  • Keyboard shortcuts:
    • Ctrl+U / Ctrl+K Clear text before/after cursor
    • Ctrl+A / Ctrl+E Jump left/right by one word
  • .clam.settings allows changing of prompt colors and adding of aliases
  • Several bugs fixed.
comp500 #10
Posted 11 April 2015 - 08:38 AM
what license is this under? I'd like to fork it if possible. MIT is best for me
Edited on 11 April 2015 - 06:38 AM
ElvishJerricco #11
Posted 11 April 2015 - 06:09 PM
what license is this under? I'd like to fork it if possible. MIT is best for me

Squid I've kinda left this project mostly to you. Do you want to add a license?
SquidDev #12
Posted 12 April 2015 - 12:58 PM
what license is this under? I'd like to fork it if possible. MIT is best for me

Squid I've kinda left this project mostly to you. Do you want to add a license?

Yeah, I'll add a MIT to it now. However it would be great if you could send a pull request if you are making fixes/minor changes, though that of course is up to you.
Edited on 12 April 2015 - 11:01 AM
comp500 #13
Posted 12 April 2015 - 09:29 PM
what license is this under? I'd like to fork it if possible. MIT is best for me

Squid I've kinda left this project mostly to you. Do you want to add a license?

Yeah, I'll add a MIT to it now. However it would be great if you could send a pull request if you are making fixes/minor changes, though that of course is up to you.
noo.. no fixes or minor changes. just a complete rewrite new shell thing, borrowing code from you guys :D/>
anyway, thanks for adding a license
Edited on 12 April 2015 - 07:31 PM
SquidDev #14
Posted 12 July 2015 - 04:37 PM
Just a minor update for 1.2: There have been some bug fixes, and autocompletion is now implemented, using the same method as CC 1.74.
SquidDev #15
Posted 15 July 2015 - 09:26 PM
Just pushed another update, we now have a hard dependency on CC 1.74, but rendering is now faster due to term.blit. The improved read command, as used in ClamShell is used by default everywhere now. I've also fixed the buffer: it now obeys term_resize and so should work with multishell. For those who don't know what ClamShell looks like - have some screenies:

Multishell and autocompletion


Scrollback
biggest yikes #16
Posted 20 July 2015 - 06:13 PM
I love how if you're in a directory and your power goes out or something, when you run your computer again you'll be at the same directory with the same history as before.
SquidDev #17
Posted 20 July 2015 - 06:37 PM
I love how if you're in a directory and your power goes out or something, when you run your computer again you'll be at the same directory with the same history as before.

Thanks! I think that was my first PR to the project. Both current directory and command history are preserved across reboots which is nice.
biggest yikes #18
Posted 20 July 2015 - 08:09 PM
Thanks! I think that was my first PR to the project. Both current directory and command history are preserved across reboots which is nice.
Only reason I noticed it was because .clam.session is the only debris ClamShell leaves behind >_>
MKlegoman357 #19
Posted 21 July 2015 - 10:45 AM
I noticed that in ClamShell you can scroll through the screen, and that's awesome! I wanted to add support for this in my command-line utility Pack. Currently, when listing commands I rely on textutils.pagedPrint so the help would not be cut off (it's relatively big). But in ClamShell you can scroll by yourself so I figured that it's not really needed there. My question is: what's the best way to detect if my program is running in ClamShell?
Edited on 21 July 2015 - 08:46 AM
biggest yikes #20
Posted 21 July 2015 - 03:30 PM
My question is: what's the best way to detect if my program is running in ClamShell?
here's a couple ways:

local clamshell = fs.exists(".clam.session")

local clamshell = shell.path():find("ClamShell") ~= nil
the first one detects if ClamShell is installed, the second one detects if ClamShell is running.
Edited on 21 July 2015 - 01:41 PM
MKlegoman357 #21
Posted 21 July 2015 - 04:15 PM
here's a couple ways:

local clamshell = fs.exists(".clam.session")

local clamshell = shell.path():find("ClamShell") ~= nil
the first one detects if ClamShell is installed, the second one detects if ClamShell is running.

Yeah, I could figure out these kind of 'hacks' myself, but thought maybe SquidDev has a better idea, as he knows the code better.
SquidDev #22
Posted 21 July 2015 - 07:10 PM
I noticed that in ClamShell you can scroll through the screen, and that's awesome! I wanted to add support for this in my command-line utility Pack. Currently, when listing commands I rely on textutils.pagedPrint so the help would not be cut off (it's relatively big). But in ClamShell you can scroll by yourself so I figured that it's not really needed there. My question is: what's the best way to detect if my program is running in ClamShell?

There isn't a de facto way of doing it. ClamShell provides a series of additional shell methods so I guess you could do

if shell.getEnvironmentVariables then
-- ClamShell
end

ClamShell also provides the stdin, stdout and stderr streams, so you could also do:


if stdout and stdout.write then
  stdout.write(text) -- Or just write(text) or print(text)/stdout.writeLine(text). They are pretty much identical.
else
  textutils.pagedPrint(text)
end

Using the print style functions has the advantage that you can pipe to an output (such as glep it or redirect to a file).
Edited on 21 July 2015 - 06:59 PM
SquidDev #23
Posted 21 February 2016 - 07:00 PM
I've just released another version of ClamShell. This is mostly a bug fix update but adds some new features too:
  • Fix autocompletion on file names
  • Allow scrolling from any read function
  • Add custom lua program - this is actually taken from another project I'm working on, but I bunged it into ClamShell too.
  • Highlight matches in glep
Just for fun I though I'd show off some of the basic things ClamShell can do:



Thanks to BombBloke's recgif!
Edited on 21 February 2016 - 06:02 PM
ElvishJerricco #24
Posted 22 February 2016 - 04:49 AM
Awesome changes! Feel free to send me anything you'd like me to add / change / rewrite-from-scratch in the OP =P
SquidDev #25
Posted 29 February 2016 - 09:25 PM
I've released another update. This one deserves a whole "minor" version because it has a whole load of new features.

Parser rewrite
The parser and interpreter got a rewrite. This adds support for && and ||:

test -d file || echo "Nope"
mv foo bar && cat bar
I've also added a for loop. This iterates through each line in the output of another command:

for file = ls {
  if test -f $file {
	echo $file
  }
}
There is also support for inline/nested commands:

ls $(pwd)
cat "$(pwd)/foo/bar"

New commands
I've added several new commands:
  • wc - counts words, characters, lines and max line length in one or more files
  • sleep - sleeps for x seconds
  • pwd - print current directory.
  • timeit - time the execution of another command
Custom prompt functions
Inspired by a zsh theme, you can add custom prompt functions by creating a .clam.prompt file in the form:

return { prompt = function(shell, previousSuccess, settings) write(">") end
This can be used to display custom prompts. In my case I show the success of the previous command with a small dot:
Edited on 29 February 2016 - 08:26 PM
Creator #26
Posted 29 February 2016 - 09:49 PM
What would one use the for loop for? File iteration in the shell? Also, logic operators?

I am not complaining, but why?

Else, awesome!
SquidDev #27
Posted 01 March 2016 - 07:34 AM
What would one use the for loop for? File iteration in the shell? Also, logic operators?

To be honest, I've only ever used pipes and redirects to files. However someone might find a use for them…