You might be interested in an experiment of mine, https://github.com/tonyg/racket-something/blob/master/src/so..., which combines an indentation-based reader with a handler for unbound variables to permit fluid interoperation between Racket procedures and external programs.
#lang something/shell
// Simple demos
ls -la $HOME | grep "^d" | fgrep -v "."
ls -la | wc -l | read-line |> string-split |> car |> string->number |> \
printf "There are ~a lines here." | sed -e "s: are : seem to be :"
(newline)
def ps-output
pipeline
ps -wwwax
preserve-header 1 {: grep "racket" }
space-separated-columns [string->number]
|> csv-expr->table
print ps-output
def message-box text:
whiptail --title "Testing" --ok-button "OK" --msgbox text 8 50
message-box "This is pretty cool."
`ls`, `grep`, `whiptail` and so on are unbound Racket variables, and the macros forming part of the `something/shell` dialect replace them with calls to the external programs, sorting out the plumbing, pipes and so on. `read-line`, `car`, `string-split`, etc are ordinary Racket functions from the standard libraries.
(EDIT): Here's a screencast of an interactive session with the shell. Very simple, but shows some of the basics: https://asciinema.org/a/83450
Fun Unix Trivia Time: Touch is supposed to update modified times. That it creates files when they don't exist is only the most common use, not the point of the thing.
Thanks! I implemented this just to create blank files, and didn't bother researching correctness. That's exactly the kind of correction I was hoping for when posting to HN. My first post too.
Another minor issue: cd without arguments should switch to the home directory rather than display the current directory (as your Readme suggests you do). You already have pwd for displaying the current directory.
I suppose this is a similar situation as with `cat' - it's meant to conCATenate stuff, but the most common usage I've seen (and one I've learned) is the idiom of `cat somefile | some-commands'.
(Whenever I share a snippet with this idiom, I get told by one of my cow-orkers to stop abusing kittens...)
Oh, the update times feature is pretty common, at least among those who use a shared system where "scratch" files are deleted when they are seven days old.
Funny thing. I work in the computational field where we deal with TBs of data regularly. We are supposed to backup sim outputs to tape asap and using touch to modify modification times to avoid this is explicitly forbidden.
This reminds me of my time working with the Scheme Shell scsh!
From the readme I don’t quite understand the purpose of lsh, though. Usually, a shell allows you to run programs and manipulate their environment. However, lsh seems to reimplement some commands like find or rm and does not provide operators to manipulate the runtime environment like redirecting output. Maybe you can write in the readme your goal: play with racket, have a shell that accepts racket forms or something different.
It would be nice to see some examples of the syntax, maybe a couple of simple scripts...
E.g., the README states that 'cd/' is an alias for '(cd "/")' which seems to imply that one has to wrap every command in '()' and quote every filename... not very practical for a shell!
Thanks for the feedback, I will update the description ASAP. I know I need to write a few examples, especially how to handle files. If compiled with pmap, it's possible to do things like:
...and run any binary against all files in the current directory, in parallel. (directory-list) returns a list of files and one can run any Racket function against that list. It's also possible to gather any command output and pipe it back to Racket for processing. I'm working on a more polished build with better docs.
I'd like to cover the minimum (like running binaries directly instead of having to use run). I've only begun to think about everything that's needed to bring this to its full potential.
I'll also check the other projects I hadn't heard of, maybe some of them are already far better than mine :)
Thank you for writing. Agreed, there a need for direction and better documentation. Feedback has been great and this has motivated me to work harder at this. I’d like to have something more standard. I currently use lsh to walk paths and record macros I use in IO tools for production.
For the Emacs users, there's eshell - a Shell, implemented in Elisp with the common GNU tooling, but also with the option to hook into regular elisp functions. Pretty cool stuff!
With the major caveat that piping commands together doesn't work that well, eshell has been an amazingly useful feature of emacs for me. Combined with TRAMP support, it makes dealing with remote machines much much more friendly. As an example, "cd 'ssh:firstmachine|ssh:secondmachine:/'" works just like you would anticipate, bounces you through first machine to second machine. All commands from that point are executed as if you were on second machine. You can even run "shell" to drop to a more traditional shell at that point. Even better, "cp someFile ~/" will transmit a file back to my local machine so that I don't have to work with the scp commands that it requires. :)
I want to like Eshell, but it's hard to get past the fact that it has existed for almost 20 years and still doesn't support basic input redirection. Modern shells like Fish and Zsh are far more polished and convenient command-lines.
I really like seeing more people use Lisp, and it's neat to see what other people come up with, but I'll stick with Emacs, Slime and occasionally eshell.
More often than not it's easiest to just use the Common Lisp functions for creating directories and interacting with the system, but when that doesn't work, I have a function similar to this one in my .sbclrc file:
(defun run (cmd)
(with-output-to-string (outs)
(uiop:run-program cmd :output outs)))
At work, I've even created a small Common Lisp library for calling our JSON APIs over HTTPS and running commands on our test clusters using SSH. It's all tightly integrated into Emacs, and I can use it do things like open remote files in my local Emacs.
I started lsh a few weeks before finding out about Rash. I found it too complicated to be used as a replacement for sh, but I might not have read enough about it to make a proper judgment. In the end I wanted to implement something that behaved exactly like I'd expect to.
I'm late to the party, but I'm the author of Rash. Rash's documentation is terrible and out of date, but I've actually been planning to redo/improve all of the documentation within a week or two. I'm hoping that once I do that it will be much more approachable. Rash tries to allow both Bash style things and more normal Racket to be done with ease and mixed, and a lot of the complexity of Rash has to do with getting those two things to work together, and to allow lots of macro extensibility. But I think you'll find that it's quite powerful and useful... once I make it easier to make sense of.
Of course I'm also happy for anyone to make a new shell that they like and want to use themselves. I did it, after all...
I was going to comment about this! The author is a student at my school who studies under Matthew Flatt (so no surprise it's based on Racket). I've worked with him on class projects before and he's not only wicked smart but also a really nice guy.
This lets you read environment variables like $home, and write to them like ordinary variables, eg (setf $message "hello"). It reads the variable name as a Lisp symbol, so by default it will be upcased; this happens to be convenient for environment variables, but as with any symbols in Lisp, you can escape lowercase characters using either \ before the character or |'s around the whole symbol, or you could change the readtable case, or just preserve case initially in the read macro.
The most memorable thing about Scsh for me is its acknowledgments rant, which starts:
"Who should I thank? My so-called "colleagues," who laugh at me behind my back, all the while becoming famous on my work? My worthless graduate students, whose computer skills appear to be limited to downloading bitmaps off of netnews? My parents, who are still waiting for me to quit "fooling around with computers," go to med school, and become a radiologist? My department chairman, a manager who gives one new insight into and sympathy for disgruntled postal workers?"
and concludes:
"Oh yes, the acknowledgements. I think not. I did it. I did it all, by myself."
Here's an example (https://github.com/tonyg/racket-something/blob/master/exampl...):
`ls`, `grep`, `whiptail` and so on are unbound Racket variables, and the macros forming part of the `something/shell` dialect replace them with calls to the external programs, sorting out the plumbing, pipes and so on. `read-line`, `car`, `string-split`, etc are ordinary Racket functions from the standard libraries.(EDIT): Here's a screencast of an interactive session with the shell. Very simple, but shows some of the basics: https://asciinema.org/a/83450
(Whenever I share a snippet with this idiom, I get told by one of my cow-orkers to stop abusing kittens...)
From the readme I don’t quite understand the purpose of lsh, though. Usually, a shell allows you to run programs and manipulate their environment. However, lsh seems to reimplement some commands like find or rm and does not provide operators to manipulate the runtime environment like redirecting output. Maybe you can write in the readme your goal: play with racket, have a shell that accepts racket forms or something different.
E.g., the README states that 'cd/' is an alias for '(cd "/")' which seems to imply that one has to wrap every command in '()' and quote every filename... not very practical for a shell!
(pmap (lambda (f) (run (+ "[some-command] " f))) (directory-list))
...and run any binary against all files in the current directory, in parallel. (directory-list) returns a list of files and one can run any Racket function against that list. It's also possible to gather any command output and pipe it back to Racket for processing. I'm working on a more polished build with better docs.
Did not know that. That’s a neat trick! Do feel free to share more if you have any :)
$ (defun sq (x) (* x x))
$ sq 5
25
More often than not it's easiest to just use the Common Lisp functions for creating directories and interacting with the system, but when that doesn't work, I have a function similar to this one in my .sbclrc file:
(defun run (cmd) (with-output-to-string (outs) (uiop:run-program cmd :output outs)))
At work, I've even created a small Common Lisp library for calling our JSON APIs over HTTPS and running commands on our test clusters using SSH. It's all tightly integrated into Emacs, and I can use it do things like open remote files in my local Emacs.
Note: I am not the author
Of course I'm also happy for anyone to make a new shell that they like and want to use themselves. I did it, after all...
"Who should I thank? My so-called "colleagues," who laugh at me behind my back, all the while becoming famous on my work? My worthless graduate students, whose computer skills appear to be limited to downloading bitmaps off of netnews? My parents, who are still waiting for me to quit "fooling around with computers," go to med school, and become a radiologist? My department chairman, a manager who gives one new insight into and sympathy for disgruntled postal workers?"
and concludes:
"Oh yes, the acknowledgements. I think not. I did it. I did it all, by myself."
http://philip.greenspun.com/wtr/dead-trees/acknowledgments.h...
Dead Comment