├── .gitignore ├── LICENSE ├── README.md ├── dune ├── dune-project ├── osh.ml ├── run-tests.sh ├── test-osh.sh └── tests ├── 1.desc ├── 1.err ├── 1.in ├── 1.out ├── 1.rc ├── 1.run ├── 10.desc ├── 10.err ├── 10.in ├── 10.out ├── 10.rc ├── 10.run ├── 11.desc ├── 11.err ├── 11.in ├── 11.out ├── 11.rc ├── 11.run ├── 12.desc ├── 12.err ├── 12.in ├── 12.out ├── 12.rc ├── 12.run ├── 13.desc ├── 13.err ├── 13.in ├── 13.out ├── 13.rc ├── 13.run ├── 14.desc ├── 14.err ├── 14.in ├── 14.out ├── 14.rc ├── 14.run ├── 15.desc ├── 15.err ├── 15.in ├── 15.out ├── 15.rc ├── 15.run ├── 16.desc ├── 16.err ├── 16.in ├── 16.out ├── 16.rc ├── 16.run ├── 17.desc ├── 17.err ├── 17.in ├── 17.out ├── 17.rc ├── 17.run ├── 18.desc ├── 18.err ├── 18.in ├── 18.out ├── 18.rc ├── 18.run ├── 19.desc ├── 19.err ├── 19.in ├── 19.out ├── 19.rc ├── 19.run ├── 2.desc ├── 2.err ├── 2.in ├── 2.out ├── 2.rc ├── 2.run ├── 20.desc ├── 20.err ├── 20.in ├── 20.out ├── 20.rc ├── 20.run ├── 21.desc ├── 21.err ├── 21.in ├── 21.out ├── 21.rc ├── 21.run ├── 22.desc ├── 22.err ├── 22.in ├── 22.out ├── 22.rc ├── 22.run ├── 3.desc ├── 3.err ├── 3.in ├── 3.out ├── 3.pre ├── 3.rc ├── 3.run ├── 4.desc ├── 4.err ├── 4.in ├── 4.out ├── 4.rc ├── 4.run ├── 5.desc ├── 5.err ├── 5.in ├── 5.out ├── 5.rc ├── 5.run ├── 6.desc ├── 6.err ├── 6.in ├── 6.out ├── 6.rc ├── 6.run ├── 7.desc ├── 7.err ├── 7.in ├── 7.out ├── 7.rc ├── 7.run ├── 8.desc ├── 8.err ├── 8.in ├── 8.out ├── 8.rc ├── 8.run ├── 9.desc ├── 9.err ├── 9.in ├── 9.out ├── 9.rc ├── 9.run ├── p1.sh ├── p2.sh ├── p2a-test ├── test1 ├── test2 ├── test3 └── test4 ├── p3.sh ├── p4.sh └── p5.sh /.gitignore: -------------------------------------------------------------------------------- 1 | osh 2 | tests-out/* 3 | *.annot 4 | *.cmo 5 | *.cma 6 | *.cmi 7 | *.a 8 | *.o 9 | *.cmx 10 | *.cmxs 11 | *.cmxa 12 | 13 | # ocamlbuild working directory 14 | _build/ 15 | 16 | # ocamlbuild targets 17 | *.byte 18 | *.native 19 | 20 | # oasis generated files 21 | setup.data 22 | setup.log 23 | 24 | # Merlin configuring file for Vim and Emacs 25 | .merlin 26 | 27 | # Dune generated files 28 | *.install 29 | 30 | # Local OPAM switch 31 | _opam/ 32 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Joe Thomas 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # simple-ocaml-shell 2 | 3 | This repo implements a simple shell (`osh`) in OCaml. I wrote it in 4 | June of 2021, while I was at the [Recurse 5 | Center](https://www.recurse.com/), in order to learn more about 6 | systems programming in a strongly-typed functional programming 7 | language. 8 | 9 | I wrote an accompanying [blog 10 | post](https://jsthomas.github.io/ocaml-shell.html) that contains a bit 11 | more exposition about how I approached this project. 12 | 13 | ## How do I build and test the project? 14 | 15 | Assuming you have `dune` [installed](https://dune.build/install), you 16 | should be able to build the shell by running 17 | 18 | ``` 19 | dune build 20 | mv _build/default/osh.exe osh 21 | ``` 22 | 23 | I built this project using version 4.11.1 of the compiler. 24 | 25 | 26 | The second command isn't strictly necessary, I just prefer to use the 27 | program without it's default `.exe` extension. 28 | 29 | After building, you can run the tests like this: `./test-osh.sh`. 30 | 31 | ## Example Run 32 | 33 | `osh` has both an interactive and a batch mode. Here is an example of the interactive mode: 34 | ``` 35 | jthomas@raven:~/repos/simple-ocaml-shell$ ./osh 36 | osh> ls 37 | _build dune dune-project example.sh LICENSE osh osh.ml '#README.md#' README.md test-osh.sh tests 38 | osh> echo "Hello World!" > world.txt 39 | osh> cat world.txt 40 | "Hello World!" 41 | osh> pwd & echo "That's the working directory, alright." 42 | /home/jthomas/repos/simple-ocaml-shell 43 | "That's the working directory, alright." 44 | osh> 45 | ``` 46 | 47 | As with more full-featured shells, `osh` supports the ability to 48 | redirect output (using `>`) and run several commands in parallel 49 | (using `&`). For simplicity, `>` redirects both `stdout` and `stderr` 50 | to the same file. 51 | 52 | `osh` can be run in batch mode like this `osh `. 53 | 54 | Because `osh` doesn't support environment variables, it has some 55 | peculiar builtins. The `path` builtin allows you to set one or more 56 | locations where executables can be found. For example: 57 | 58 | ``` 59 | osh> ls 60 | _build dune dune-project example.sh LICENSE osh osh.ml README.md test-osh.sh tests world.txt 61 | osh> path 62 | osh> ls 63 | An error has occurred 64 | osh> path /usr/bin /bin 65 | osh> ls 66 | _build dune dune-project example.sh LICENSE osh osh.ml '#README.md#' README.md test-osh.sh tests world.txt 67 | ``` 68 | 69 | This example illustrates another simplification: `osh` does not show 70 | detailed output when an error occurs (in this case, `ls` was not 71 | found). 72 | 73 | ## Background 74 | 75 | The design and tests for this project come from the book [Operating 76 | Systems: Three Easy Pieces](https://pages.cs.wisc.edu/~remzi/OSTEP/) 77 | by Remzi H. Arpaci-Dusseau and Andrea C. Arpaci-Dusseau. You can find 78 | the original project description 79 | [here](https://github.com/remzi-arpacidusseau/ostep-projects/tree/master/processes-shell). 80 | 81 | Normally, I wouldn't post solutions to exercises from a class. Since 82 | most classes will use C for this type of project, I don't think I'm 83 | creating much of an opportunity for cheating by posting an OCaml 84 | implementation. 85 | -------------------------------------------------------------------------------- /dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name osh) 3 | (libraries unix angstrom)) 4 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.7) 2 | -------------------------------------------------------------------------------- /osh.ml: -------------------------------------------------------------------------------- 1 | let print_error () = 2 | Printf.fprintf stderr "An error has occurred\n"; 3 | flush stderr 4 | 5 | 6 | type exec = { 7 | executable: string; 8 | arguments: string list; 9 | output: string option; 10 | } 11 | 12 | 13 | type line = 14 | | NoOp 15 | | ChangeDirectory of string 16 | | PathChange of string list 17 | | Executable of exec list 18 | | Quit 19 | 20 | 21 | module Parser = struct 22 | open Angstrom 23 | 24 | let is_whitespace = function 25 | | '\x20' | '\x0a' | '\x0d' | '\x09' -> true 26 | | _ -> false 27 | 28 | let is_token_char = function 29 | | '&' | '>' -> false 30 | | x -> not @@ is_whitespace x 31 | 32 | let ws = take_while is_whitespace 33 | let token = take_while1 is_token_char <* ws 34 | >>= (fun tok -> 35 | if tok = "cd" then fail "cd is not a valid token" 36 | else return tok 37 | ) 38 | 39 | let amp = char '&' <* ws 40 | let redir = char '>' <* ws 41 | let quit = string "exit" <* ws 42 | let cd = string "cd" <* ws 43 | let path = string "path" <* ws 44 | 45 | let exec_parser = 46 | many token >>= (fun tokens -> match tokens with 47 | | [] -> return @@ None 48 | | executable::arguments -> 49 | choice [ 50 | redir *> token >>= (fun filename -> 51 | return @@ Some {executable; arguments; output=Some filename}); 52 | return @@ Some {executable; arguments; output=None} 53 | ] 54 | ) 55 | 56 | let shell_parser = 57 | choice [ 58 | quit *> return Quit; 59 | cd *> token >>= (fun dir -> return @@ ChangeDirectory dir); 60 | path *> many token >>= (fun paths -> return @@ PathChange paths); 61 | sep_by amp exec_parser >>= ( 62 | fun execs -> 63 | let notnulls = List.filter Option.is_some execs in 64 | let values = List.map Option.get notnulls in 65 | return @@ Executable values 66 | ) 67 | ] 68 | 69 | let parse_line l = 70 | match String.trim l with 71 | | "" -> Result.Ok NoOp 72 | | trimmed -> Angstrom.parse_string ~consume:All shell_parser trimmed 73 | end 74 | 75 | 76 | let prompt_stream = 77 | let f _ = 78 | Printf.printf "osh> "; 79 | try 80 | Some (read_line ()) 81 | with End_of_file -> exit 0 82 | in 83 | Stream.from f 84 | 85 | 86 | let file_stream filename = 87 | let in_channel = 88 | try 89 | open_in filename 90 | with Sys_error _ -> 91 | (* If the batch file isn't present, bail. *) 92 | print_error (); 93 | exit 1 94 | in 95 | let f _ = 96 | try 97 | Some (input_line in_channel) 98 | with End_of_file -> close_in in_channel; None 99 | in 100 | Stream.from f 101 | 102 | 103 | let change_directory dirname = 104 | try 105 | Unix.chdir dirname 106 | with _ -> 107 | print_error () 108 | 109 | 110 | let executable_present full_path = 111 | try 112 | Unix.access full_path [Unix.X_OK]; 113 | true 114 | with _ -> 115 | false 116 | 117 | 118 | let run_executable path {executable; arguments; output} = 119 | let full_paths = List.map (fun p -> p ^ "/" ^ executable) path in 120 | let executables = List.append full_paths [executable] in 121 | let present = List.filter executable_present executables in 122 | 123 | match present with 124 | | [] -> print_error (); None 125 | | x :: _ -> 126 | match Unix.fork () with 127 | | 0 -> (* Child *) 128 | (* Printf.printf "Launching %s, Arguments: %s, Output: %s\n" 129 | x (String.concat ", " (executable::arguments)) (Option.value ~default:"None" output); 130 | flush stdout; 131 | *) 132 | let out = match output with 133 | | Some filename -> Unix.openfile filename [Unix.O_TRUNC; Unix.O_WRONLY; Unix.O_CREAT ] 0o640 134 | | None -> Unix.stdout 135 | in 136 | let err = match output with 137 | | None -> Unix.stderr 138 | | _ -> out 139 | in 140 | Unix.dup2 out Unix.stdout; 141 | Unix.dup2 err Unix.stderr; 142 | Unix.execv x @@ Array.of_list @@ executable::arguments; 143 | | child_pid -> (* Parent *) 144 | Some child_pid 145 | 146 | 147 | let run_executables path execs = 148 | let wait p = match p with 149 | | None -> () 150 | | Some pid -> 151 | (* Printf.printf "Waiting on %d." pid; 152 | * flush stdout; *) 153 | let _ = Unix.waitpid [] pid in 154 | () 155 | in 156 | List.iter wait @@ List.map (run_executable path) execs 157 | 158 | 159 | let amend_path cwd p = 160 | if '/' == String.get p 0 161 | then p 162 | else cwd ^ "/" ^ p 163 | 164 | 165 | let main stream = 166 | let path = ref ["/bin"] in 167 | let process text = 168 | match Parser.parse_line text with 169 | | Error _ -> print_error () 170 | | Ok (NoOp) -> () (* Empty lines should just be treated as no-ops. *) 171 | | Ok (Quit) -> exit 0 172 | | Ok (ChangeDirectory x) -> change_directory x 173 | | Ok (PathChange paths) -> 174 | let cwd = Unix.getcwd () in 175 | path := List.map (amend_path cwd) paths 176 | | Ok (Executable execs) -> run_executables !path execs 177 | in 178 | Stream.iter process stream 179 | 180 | 181 | let () = 182 | match Array.to_list Sys.argv with 183 | | _ :: [] -> main prompt_stream 184 | | _ :: filename :: [] -> main @@ file_stream filename 185 | | _ -> print_error (); 186 | exit 1 187 | -------------------------------------------------------------------------------- /run-tests.sh: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env bash 2 | 3 | # run_test testdir testnumber 4 | run_test () { 5 | local testdir=$1 6 | local testnum=$2 7 | local verbose=$3 8 | 9 | # pre: execute this after before the test is done, to set up 10 | local prefile=$testdir/$testnum.pre 11 | if [[ -f $prefile ]]; then 12 | eval $(cat $prefile) 13 | if (( $verbose == 1 )); then 14 | echo -n "pre-test: " 15 | cat $prefile 16 | fi 17 | fi 18 | local testfile=$testdir/$testnum.run 19 | if (( $verbose == 1 )); then 20 | echo -n "test: " 21 | cat $testfile 22 | fi 23 | eval $(cat $testfile) > tests-out/$testnum.out 2> tests-out/$testnum.err 24 | echo $? > tests-out/$testnum.rc 25 | 26 | # post: execute this after the test is done, to clean up 27 | local postfile=$testdir/$testnum.post 28 | if [[ -f $postfile ]]; then 29 | eval $(cat $postfile) 30 | if (( $verbose == 1 )); then 31 | echo -n "post-test: " 32 | cat $postfile 33 | fi 34 | fi 35 | return 36 | } 37 | 38 | print_error_message () { 39 | local testnum=$1 40 | local contrunning=$2 41 | local filetype=$3 42 | builtin echo -e "\e[31mtest $testnum: $filetype incorrect\e[0m" 43 | echo " what results should be found in file: $testdir/$testnum.$filetype" 44 | echo " what results produced by your program: tests-out/$testnum.$filetype" 45 | echo " compare the two using diff, cmp, or related tools to debug, e.g.:" 46 | echo " prompt> diff $testdir/$testnum.$filetype tests-out/$testnum.$filetype" 47 | if (( $contrunning == 0 )); then 48 | exit 1 49 | fi 50 | } 51 | 52 | # check_test testdir testnumber contrunning out/err 53 | check_test () { 54 | local testdir=$1 55 | local testnum=$2 56 | local contrunning=$3 57 | local filetype=$4 58 | 59 | # option to use cmp instead? 60 | returnval=$(diff $testdir/$testnum.$filetype tests-out/$testnum.$filetype) 61 | if (( $? == 0 )); then 62 | echo 0 63 | else 64 | echo 1 65 | fi 66 | } 67 | 68 | # run_and_check testdir testnumber contrunning verbose printerror 69 | # testnumber: the test to run and check 70 | # printerrer: if 1, print an error if test does not exist 71 | run_and_check () { 72 | local testdir=$1 73 | local testnum=$2 74 | local contrunning=$3 75 | local verbose=$4 76 | local failmode=$5 77 | 78 | if [[ ! -f $testdir/$testnum.run ]]; then 79 | if (( $failmode == 1 )); then 80 | echo "test $testnum does not exist" >&2; exit 1 81 | fi 82 | exit 0 83 | fi 84 | if (( $verbose == 1 )); then 85 | echo -n -e "\e[33mrunning test $testnum: \e[0m" 86 | cat $testdir/$testnum.desc 87 | fi 88 | run_test $testdir $testnum $verbose 89 | rccheck=$(check_test $testdir $testnum $contrunning rc) 90 | outcheck=$(check_test $testdir $testnum $contrunning out) 91 | errcheck=$(check_test $testdir $testnum $contrunning err) 92 | othercheck=0 93 | if [[ -f $testdir/$testnum.other ]]; then 94 | othercheck=$(check_test $testdir $testnum $contrunning other) 95 | fi 96 | # echo "results: outcheck:$outcheck errcheck:$errcheck" 97 | if (( $rccheck == 0 )) && (( $outcheck == 0 )) && (( $errcheck == 0 )) && (( $othercheck == 0 )); then 98 | builtin echo -e "\e[32mtest $testnum: passed\e[0m" 99 | if (( $verbose == 1 )); then 100 | echo "" 101 | fi 102 | else 103 | if (( $rccheck == 1 )); then 104 | print_error_message $testnum $contrunning rc 105 | fi 106 | if (( $outcheck == 1 )); then 107 | print_error_message $testnum $contrunning out 108 | fi 109 | if (( $errcheck == 1 )); then 110 | print_error_message $testnum $contrunning err 111 | fi 112 | if (( $othercheck == 1 )); then 113 | print_error_message $testnum $contrunning other 114 | fi 115 | fi 116 | } 117 | 118 | # usage: call when args not parsed, or when help needed 119 | usage () { 120 | echo "usage: run-tests.sh [-h] [-v] [-t test] [-c] [-s] [-d testdir]" 121 | echo " -h help message" 122 | echo " -v verbose" 123 | echo " -t n run only test n" 124 | echo " -c continue even after failure" 125 | echo " -s skip pre-test initialization" 126 | echo " -d testdir run tests from testdir" 127 | return 0 128 | } 129 | 130 | # 131 | # main program 132 | # 133 | verbose=0 134 | testdir="tests" 135 | contrunning=0 136 | skippre=0 137 | specific="" 138 | 139 | args=`getopt hvsct:d: $*` 140 | if [[ $? != 0 ]]; then 141 | usage; exit 1 142 | fi 143 | 144 | set -- $args 145 | for i; do 146 | case "$i" in 147 | -h) 148 | usage 149 | exit 0 150 | shift;; 151 | -v) 152 | verbose=1 153 | shift;; 154 | -c) 155 | contrunning=1 156 | shift;; 157 | -s) 158 | skippre=1 159 | shift;; 160 | -t) 161 | specific=$2 162 | shift 163 | number='^[0-9]+$' 164 | if ! [[ $specific =~ $number ]]; then 165 | usage 166 | echo "-t must be followed by a number" >&2; exit 1 167 | fi 168 | shift;; 169 | -d) 170 | testdir=$2 171 | shift 172 | shift;; 173 | --) 174 | shift; break;; 175 | esac 176 | done 177 | 178 | # need a test directory; must be named "tests-out" 179 | if [[ ! -d tests-out ]]; then 180 | mkdir tests-out 181 | fi 182 | 183 | # do a one-time setup step 184 | if (( $skippre == 0 )); then 185 | if [[ -f tests/pre ]]; then 186 | builtin echo -e "\e[33mdoing one-time pre-test\e[0m (use -s to suppress)" 187 | source tests/pre 188 | if (( $? != 0 )); then 189 | echo "pre-test: failed" 190 | exit 1 191 | fi 192 | echo "" 193 | fi 194 | fi 195 | 196 | # run just one test 197 | if [[ $specific != "" ]]; then 198 | run_and_check $testdir $specific $contrunning $verbose 1 199 | exit 0 200 | fi 201 | 202 | # run all tests 203 | (( testnum = 1 )) 204 | while true; do 205 | run_and_check $testdir $testnum $contrunning $verbose 0 206 | (( testnum = $testnum + 1 )) 207 | done 208 | 209 | exit 0 210 | -------------------------------------------------------------------------------- /test-osh.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | 3 | cp _build/default/osh.exe osh 4 | 5 | if ! [[ -x osh ]]; then 6 | echo "osh executable does not exist" 7 | exit 1 8 | fi 9 | 10 | ./run-tests.sh $* 11 | -------------------------------------------------------------------------------- /tests/1.desc: -------------------------------------------------------------------------------- 1 | Input to check bad cd. No arguments are passed to cd. 2 | -------------------------------------------------------------------------------- /tests/1.err: -------------------------------------------------------------------------------- 1 | An error has occurred 2 | -------------------------------------------------------------------------------- /tests/1.in: -------------------------------------------------------------------------------- 1 | cd 2 | exit 3 | -------------------------------------------------------------------------------- /tests/1.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/1.out -------------------------------------------------------------------------------- /tests/1.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/1.run: -------------------------------------------------------------------------------- 1 | ./osh tests/1.in 2 | -------------------------------------------------------------------------------- /tests/10.desc: -------------------------------------------------------------------------------- 1 | Redirection with multiple '>' 2 | -------------------------------------------------------------------------------- /tests/10.err: -------------------------------------------------------------------------------- 1 | An error has occurred 2 | -------------------------------------------------------------------------------- /tests/10.in: -------------------------------------------------------------------------------- 1 | ls > output.9 > output.10 2 | exit 3 | -------------------------------------------------------------------------------- /tests/10.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/10.out -------------------------------------------------------------------------------- /tests/10.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/10.run: -------------------------------------------------------------------------------- 1 | ./osh tests/10.in 2 | -------------------------------------------------------------------------------- /tests/11.desc: -------------------------------------------------------------------------------- 1 | Normal redirection. 2 | -------------------------------------------------------------------------------- /tests/11.err: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/11.err -------------------------------------------------------------------------------- /tests/11.in: -------------------------------------------------------------------------------- 1 | ls tests/p2a-test>/tmp/output11 2 | cat /tmp/output11 3 | rm -f /tmp/output11 4 | exit 5 | -------------------------------------------------------------------------------- /tests/11.out: -------------------------------------------------------------------------------- 1 | test1 2 | test2 3 | test3 4 | test4 5 | -------------------------------------------------------------------------------- /tests/11.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/11.run: -------------------------------------------------------------------------------- 1 | ./osh tests/11.in 2 | -------------------------------------------------------------------------------- /tests/12.desc: -------------------------------------------------------------------------------- 1 | Input to check bad redirection. Contains no command before '>'. 2 | -------------------------------------------------------------------------------- /tests/12.err: -------------------------------------------------------------------------------- 1 | An error has occurred 2 | -------------------------------------------------------------------------------- /tests/12.in: -------------------------------------------------------------------------------- 1 | > output.12 2 | exit 3 | -------------------------------------------------------------------------------- /tests/12.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/12.out -------------------------------------------------------------------------------- /tests/12.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/12.run: -------------------------------------------------------------------------------- 1 | ./osh tests/12.in 2 | -------------------------------------------------------------------------------- /tests/13.desc: -------------------------------------------------------------------------------- 1 | Input file is valid and empty. But shell is invoked in batch mode with 2 files (with same file 13.in used twice) 2 | -------------------------------------------------------------------------------- /tests/13.err: -------------------------------------------------------------------------------- 1 | An error has occurred 2 | -------------------------------------------------------------------------------- /tests/13.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/13.in -------------------------------------------------------------------------------- /tests/13.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/13.out -------------------------------------------------------------------------------- /tests/13.rc: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /tests/13.run: -------------------------------------------------------------------------------- 1 | ./osh tests/13.in INPUT_DIR/13.in 2 | -------------------------------------------------------------------------------- /tests/14.desc: -------------------------------------------------------------------------------- 1 | Shell is invoked with a bad batch file. 2 | -------------------------------------------------------------------------------- /tests/14.err: -------------------------------------------------------------------------------- 1 | An error has occurred 2 | -------------------------------------------------------------------------------- /tests/14.in: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/14.in -------------------------------------------------------------------------------- /tests/14.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/14.out -------------------------------------------------------------------------------- /tests/14.rc: -------------------------------------------------------------------------------- 1 | 1 2 | -------------------------------------------------------------------------------- /tests/14.run: -------------------------------------------------------------------------------- 1 | ./osh tests/p2a-tests/bad 2 | -------------------------------------------------------------------------------- /tests/15.desc: -------------------------------------------------------------------------------- 1 | Tests command with variable whitespace. 2 | -------------------------------------------------------------------------------- /tests/15.err: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/15.err -------------------------------------------------------------------------------- /tests/15.in: -------------------------------------------------------------------------------- 1 | 2 | echo test variable whitespace! 3 | exit 4 | -------------------------------------------------------------------------------- /tests/15.out: -------------------------------------------------------------------------------- 1 | test variable whitespace! 2 | -------------------------------------------------------------------------------- /tests/15.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/15.run: -------------------------------------------------------------------------------- 1 | ./osh tests/15.in 2 | -------------------------------------------------------------------------------- /tests/16.desc: -------------------------------------------------------------------------------- 1 | Command only contains & 2 | -------------------------------------------------------------------------------- /tests/16.err: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/16.err -------------------------------------------------------------------------------- /tests/16.in: -------------------------------------------------------------------------------- 1 | & 2 | exit 3 | -------------------------------------------------------------------------------- /tests/16.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/16.out -------------------------------------------------------------------------------- /tests/16.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/16.run: -------------------------------------------------------------------------------- 1 | ./osh tests/16.in 2 | -------------------------------------------------------------------------------- /tests/17.desc: -------------------------------------------------------------------------------- 1 | Parallel command contains & at the end 2 | -------------------------------------------------------------------------------- /tests/17.err: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/17.err -------------------------------------------------------------------------------- /tests/17.in: -------------------------------------------------------------------------------- 1 | path tests 2 | p1.sh & p2.sh & 3 | exit 4 | -------------------------------------------------------------------------------- /tests/17.out: -------------------------------------------------------------------------------- 1 | test1 2 | test2 3 | test3 4 | test4 5 | test2 6 | -------------------------------------------------------------------------------- /tests/17.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/17.run: -------------------------------------------------------------------------------- 1 | ./osh tests/17.in 2 | -------------------------------------------------------------------------------- /tests/18.desc: -------------------------------------------------------------------------------- 1 | Basic test of running parallel commands. 2 | -------------------------------------------------------------------------------- /tests/18.err: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/18.err -------------------------------------------------------------------------------- /tests/18.in: -------------------------------------------------------------------------------- 1 | path tests 2 | p1.sh & p2.sh & p3.sh 3 | exit 4 | -------------------------------------------------------------------------------- /tests/18.out: -------------------------------------------------------------------------------- 1 | test1 2 | test2 3 | test3 4 | test4 5 | test2 6 | Linux 7 | -------------------------------------------------------------------------------- /tests/18.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/18.run: -------------------------------------------------------------------------------- 1 | ./osh tests/18.in 2 | -------------------------------------------------------------------------------- /tests/19.desc: -------------------------------------------------------------------------------- 1 | Parallel commands with no space between command and '&' 2 | -------------------------------------------------------------------------------- /tests/19.err: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/19.err -------------------------------------------------------------------------------- /tests/19.in: -------------------------------------------------------------------------------- 1 | path tests 2 | p1.sh&p2.sh&p3.sh 3 | exit 4 | -------------------------------------------------------------------------------- /tests/19.out: -------------------------------------------------------------------------------- 1 | test1 2 | test2 3 | test3 4 | test4 5 | test2 6 | Linux 7 | -------------------------------------------------------------------------------- /tests/19.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/19.run: -------------------------------------------------------------------------------- 1 | ./osh tests/19.in 2 | -------------------------------------------------------------------------------- /tests/2.desc: -------------------------------------------------------------------------------- 1 | 2 arguments are passed to cd. 2 | -------------------------------------------------------------------------------- /tests/2.err: -------------------------------------------------------------------------------- 1 | An error has occurred 2 | -------------------------------------------------------------------------------- /tests/2.in: -------------------------------------------------------------------------------- 1 | cd /bad1 /bad2 2 | exit 3 | -------------------------------------------------------------------------------- /tests/2.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/2.out -------------------------------------------------------------------------------- /tests/2.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/2.run: -------------------------------------------------------------------------------- 1 | ./osh tests/2.in 2 | -------------------------------------------------------------------------------- /tests/20.desc: -------------------------------------------------------------------------------- 1 | Redirection and Parallel commands combined 2 | -------------------------------------------------------------------------------- /tests/20.err: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/20.err -------------------------------------------------------------------------------- /tests/20.in: -------------------------------------------------------------------------------- 1 | path /bin tests 2 | p1.sh > /tmp/output201 & p2.sh > /tmp/output202 & p3.sh > /tmp/output203 3 | cat /tmp/output201 4 | cat /tmp/output202 5 | cat /tmp/output203 6 | rm -rf /tmp/output201 7 | rm -rf /tmp/output202 8 | rm -rf /tmp/output203 9 | exit 10 | -------------------------------------------------------------------------------- /tests/20.out: -------------------------------------------------------------------------------- 1 | test1 2 | test2 3 | test3 4 | test4 5 | test2 6 | Linux 7 | -------------------------------------------------------------------------------- /tests/20.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/20.run: -------------------------------------------------------------------------------- 1 | ./osh tests/20.in 2 | -------------------------------------------------------------------------------- /tests/21.desc: -------------------------------------------------------------------------------- 1 | Empty commands 2 | -------------------------------------------------------------------------------- /tests/21.err: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/21.err -------------------------------------------------------------------------------- /tests/21.in: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | exit 7 | -------------------------------------------------------------------------------- /tests/21.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/21.out -------------------------------------------------------------------------------- /tests/21.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/21.run: -------------------------------------------------------------------------------- 1 | ./osh tests/21.in 2 | -------------------------------------------------------------------------------- /tests/22.desc: -------------------------------------------------------------------------------- 1 | Test to check that commands are not executed serially 2 | -------------------------------------------------------------------------------- /tests/22.err: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/22.err -------------------------------------------------------------------------------- /tests/22.in: -------------------------------------------------------------------------------- 1 | path /bin tests 2 | p5.sh > /tmp/output22 & p4.sh > /tmp/output22 3 | cat /tmp/output22 4 | rm -f /tmp/output22 5 | exit 6 | -------------------------------------------------------------------------------- /tests/22.out: -------------------------------------------------------------------------------- 1 | test1 2 | test2 3 | test3 4 | test4 5 | -------------------------------------------------------------------------------- /tests/22.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/22.run: -------------------------------------------------------------------------------- 1 | ./osh tests/22.in 2 | -------------------------------------------------------------------------------- /tests/3.desc: -------------------------------------------------------------------------------- 1 | ls with a bad directory name. 2 | -------------------------------------------------------------------------------- /tests/3.err: -------------------------------------------------------------------------------- 1 | ls: cannot access '/no/such/file': No such file or directory 2 | -------------------------------------------------------------------------------- /tests/3.in: -------------------------------------------------------------------------------- 1 | ls /no/such/file 2 | exit 3 | -------------------------------------------------------------------------------- /tests/3.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/3.out -------------------------------------------------------------------------------- /tests/3.pre: -------------------------------------------------------------------------------- 1 | ls /no/such/file > tests/3.out 2> tests/3.err 2 | -------------------------------------------------------------------------------- /tests/3.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/3.run: -------------------------------------------------------------------------------- 1 | ./osh tests/3.in 2 | -------------------------------------------------------------------------------- /tests/4.desc: -------------------------------------------------------------------------------- 1 | Input to run misc. commands. 2 | -------------------------------------------------------------------------------- /tests/4.err: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/4.err -------------------------------------------------------------------------------- /tests/4.in: -------------------------------------------------------------------------------- 1 | cd tests/p2a-test 2 | ls 3 | -------------------------------------------------------------------------------- /tests/4.out: -------------------------------------------------------------------------------- 1 | test1 2 | test2 3 | test3 4 | test4 5 | -------------------------------------------------------------------------------- /tests/4.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/4.run: -------------------------------------------------------------------------------- 1 | ./osh tests/4.in 2 | -------------------------------------------------------------------------------- /tests/5.desc: -------------------------------------------------------------------------------- 1 | Tries to exit with an argument. Should throw an error. 2 | -------------------------------------------------------------------------------- /tests/5.err: -------------------------------------------------------------------------------- 1 | An error has occurred 2 | -------------------------------------------------------------------------------- /tests/5.in: -------------------------------------------------------------------------------- 1 | exit bad 2 | exit 3 | -------------------------------------------------------------------------------- /tests/5.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/5.out -------------------------------------------------------------------------------- /tests/5.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/5.run: -------------------------------------------------------------------------------- 1 | ./osh tests/5.in 2 | -------------------------------------------------------------------------------- /tests/6.desc: -------------------------------------------------------------------------------- 1 | Try running a shell script without setting path. 2 | -------------------------------------------------------------------------------- /tests/6.err: -------------------------------------------------------------------------------- 1 | An error has occurred 2 | -------------------------------------------------------------------------------- /tests/6.in: -------------------------------------------------------------------------------- 1 | p1.sh 2 | exit 3 | -------------------------------------------------------------------------------- /tests/6.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/6.out -------------------------------------------------------------------------------- /tests/6.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/6.run: -------------------------------------------------------------------------------- 1 | ./osh tests/6.in 2 | -------------------------------------------------------------------------------- /tests/7.desc: -------------------------------------------------------------------------------- 1 | Set path, run a shell script. Overwrite path and then try running the script again. 2 | -------------------------------------------------------------------------------- /tests/7.err: -------------------------------------------------------------------------------- 1 | An error has occurred 2 | An error has occurred 3 | -------------------------------------------------------------------------------- /tests/7.in: -------------------------------------------------------------------------------- 1 | path tests 2 | p1.sh 3 | path 4 | p1.sh 5 | ls 6 | exit 7 | -------------------------------------------------------------------------------- /tests/7.out: -------------------------------------------------------------------------------- 1 | test1 2 | test2 3 | test3 4 | test4 5 | -------------------------------------------------------------------------------- /tests/7.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/7.run: -------------------------------------------------------------------------------- 1 | ./osh tests/7.in 2 | -------------------------------------------------------------------------------- /tests/8.desc: -------------------------------------------------------------------------------- 1 | Redirection with no output file specified. 2 | -------------------------------------------------------------------------------- /tests/8.err: -------------------------------------------------------------------------------- 1 | An error has occurred 2 | -------------------------------------------------------------------------------- /tests/8.in: -------------------------------------------------------------------------------- 1 | ls > 2 | exit 3 | -------------------------------------------------------------------------------- /tests/8.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/8.out -------------------------------------------------------------------------------- /tests/8.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/8.run: -------------------------------------------------------------------------------- 1 | ./osh tests/8.in 2 | -------------------------------------------------------------------------------- /tests/9.desc: -------------------------------------------------------------------------------- 1 | Redirection with multiple output files. 2 | -------------------------------------------------------------------------------- /tests/9.err: -------------------------------------------------------------------------------- 1 | An error has occurred 2 | -------------------------------------------------------------------------------- /tests/9.in: -------------------------------------------------------------------------------- 1 | ls > output.9 output.10 2 | exit 3 | -------------------------------------------------------------------------------- /tests/9.out: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/9.out -------------------------------------------------------------------------------- /tests/9.rc: -------------------------------------------------------------------------------- 1 | 0 2 | -------------------------------------------------------------------------------- /tests/9.run: -------------------------------------------------------------------------------- 1 | ./osh tests/9.in 2 | -------------------------------------------------------------------------------- /tests/p1.sh: -------------------------------------------------------------------------------- 1 | #! /bin/bash 2 | cd tests/p2a-test 3 | ls 4 | -------------------------------------------------------------------------------- /tests/p2.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sleep 3 3 | cd tests/p2a-test 4 | ls test2 5 | 6 | -------------------------------------------------------------------------------- /tests/p2a-test/test1: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/p2a-test/test1 -------------------------------------------------------------------------------- /tests/p2a-test/test2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/p2a-test/test2 -------------------------------------------------------------------------------- /tests/p2a-test/test3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/p2a-test/test3 -------------------------------------------------------------------------------- /tests/p2a-test/test4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsthomas/simple-ocaml-shell/9724612846dae3a2ee36ba3054d23ec14d245a7b/tests/p2a-test/test4 -------------------------------------------------------------------------------- /tests/p3.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sleep 4 3 | echo Linux 4 | 5 | -------------------------------------------------------------------------------- /tests/p4.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo Linux 3 | -------------------------------------------------------------------------------- /tests/p5.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | sleep 4 3 | cd tests/p2a-test 4 | ls 5 | --------------------------------------------------------------------------------