Last active
April 30, 2023 03:49
-
-
Save nixpulvis/59d4f60db401f4b3fba6d6781063c7f5 to your computer and use it in GitHub Desktop.
Building a Shell - Part 1 https://nixpulvis.com/ramblings/2018-07-11-building-a-shell-part-1
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Commands to be executed directly by this shell. | |
BUILTINS = {} | |
# The builtin `cd` for changing the shell's working directory. | |
BUILTINS['cd'] = lambda do |args| | |
# Change to the home directory by default. | |
args << ENV['HOME'] if args.empty? | |
# Read the destination path, doing very basic path expansion. | |
dest = args.pop.gsub(/~/, ENV['HOME']) | |
# Try to change this shell's working directory. | |
begin | |
Dir.chdir(dest) | |
rescue Exception | |
puts("no such directory: #{dest}") | |
end | |
end | |
# The builtin to exit the shell. | |
BUILTINS['exit'] = lambda do |args| | |
# Exit with a status of 0 by default. | |
args << 0 if args.empty? | |
# Exit the shell. | |
exit args.pop.to_i | |
end | |
# Print a prompt, then read a line from STDIN. | |
def read | |
print('$ ') | |
# Try to read a line, exiting if there's no more lines. | |
line = STDIN.gets | |
if line | |
line.chomp.split(' ') | |
else | |
exit | |
end | |
end | |
# Run the given command in a subprocess. | |
def evaluate(argv) | |
if BUILTINS[argv[0]] | |
BUILTINS[argv[0]].call(argv[1..-1]) | |
else | |
success = system(*argv) | |
unless success | |
puts("unknown command '#{argv[0]}'") | |
end | |
end | |
end | |
# Don't exit on `SIGINT`, instead simply return a new prompt. | |
trap('INT') { print("\n$ ") } | |
# The glorious REPL itself. | |
loop { evaluate(read) } |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use std::io::{self, Read, Write}; | |
use std::process::{Command, Output}; | |
use std::str; | |
// Our shell, for the greater good. Ready and waiting. | |
fn main() { | |
// Standard input file descriptor (0), used for user input from the user | |
// of the shell. | |
let mut stdin = io::stdin(); | |
// Standard output file descriptor (1), used to display program output to | |
// the user of the shell. | |
let mut stdout = io::stdout(); | |
// STDIN, input buffer, used to `read` text into for further processing. | |
// TODO: Full fledged parser will be neato. | |
let mut input = [0; 24]; | |
loop { | |
// XXX: Blindly drop the contents of input, again this will be better | |
// with a real parser. | |
input = [0; 24]; | |
// Print a boring static prompt. | |
print!("oursh> "); | |
stdout.lock().flush(); | |
loop { | |
// TODO: Enable raw access to STDIN, so we can read as the user | |
// types. By default the underlying file is line buffered. This | |
// will allow us to process history, syntax, and more! | |
// Read what's avalible to us. | |
stdin.read(&mut input).expect("error reading STDIN"); | |
// Once we've read a complete "program" (§2.10.2) we handle it, | |
// until then we keep reading. Once we have a proper parser this | |
// wont look like a huge hack. | |
let vec: Vec<&[u8]> = input.splitn(2, |b| *b == '\n' as u8).collect(); | |
match &vec[..] { | |
[line, rest] => { | |
let program = str::from_utf8(&line).expect("error reading utf8"); | |
let mut output = handle_program(program); | |
stdout.write(&output.stdout).expect("error writing to STDOUT"); | |
break | |
} | |
_ => {}, | |
} | |
} | |
} | |
} | |
// Obviously very wrong. Most notably this blocks until the command completes. | |
fn handle_program(program: &str) -> Output { | |
Command::new(program) | |
.output() | |
.expect("error executing program") | |
} |
oursh
progress
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Rust 7 100 478 594
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Language files blank comment code
-------------------------------------------------------------------------------
Rust 30 296 788 2257
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Rust / Ruby Filesize equivalent.