spakman / urchin Goto Github PK
View Code? Open in Web Editor NEWA Unix shell for Ruby programmers.
Home Page: http://spakman.github.com/urchin/
License: GNU General Public License v3.0
A Unix shell for Ruby programmers.
Home Page: http://spakman.github.com/urchin/
License: GNU General Public License v3.0
Urchin === A Unix shell for Ruby programmers. If you have any ideas for what you think this should mean, please submit a ticket! Urchin does not aim to be POSIX compliant, but is heavily influenced by Bash and Zsh. Urchin aims to make Ruby a first class citizen in the shell and be a good place for experimenting with new shell ideas. Authors === Mark Somerville <[email protected]> http://mark.scottishclimbs.com/ @_Spakman Spakman on Freenode Current status === Preparing to release version 0.2.0. On Linux this will be beta quality. It is considerably less tested on other platforms. I have been using it as my login shell since version 0.1.0 and it has been pretty stable. Quite a lot of common Unix shell stuff is implemented: job control, redirections, tilde expansion, aliasing, globbing and environment variables. Functions are the most obvious thing missing from that list. These will most likely appear in 0.3.0. Straightforward inline Ruby is available! This was the original inspiration for the project (although the vision has grown somewhat now). This allows writing Ruby processes directly on the command line, without the need to escape special characters. This will likely be expanded and improved upon in later releases. I will create a gem for version 0.2.0. Current problems === GitHub is used for ticket tracking: https://github.com/Spakman/urchin/issues Requirements === Ruby --- Tested with MRI 1.8.7, 1.9.2 and 1.9.3-dev on Linux. If running using 1.9.2 on a laptop, be aware of bug #3436 (http://redmine.ruby-lang.org/issues/show/3436). This should be fixed when 1.9.3 is released. rb-readline --- Since version 0.2.0, Urchin relies on rb-readline. Use the latest version or check it out from GitHub[1]. I'm one of the maintainers for rb-readline and always check that Urchin works with the latest version. 1 - https://github.com/luislavena/rb-readline ruby-termios --- Tested with version 0.9.6. Some examples that work === Pipelines: ls -la | head -n2 Calling a new Ruby process (~@ is the default delimiter): ls --no-color |~@ puts STDIN.read.reverse ~@ | sort ls --no-color |~@ o s.reverse ~@ | sort Redirecting STDOUT, STDIN, STDERR: uptime > output uptime >> output ruby -e 'puts STDIN.read; STDERR.puts 33' < input > out_and_err 2>&1 Backgrounding jobs: sleep 60 & Multiple jobs: sleep 60 & man sleep sleep 60; echo "it's over" Quoted and unquoted parameters: grep -r '"hello"' . grep -r "\"hello\"" . ls my\ annoyingly\ named\ dir find . -name 'hello' -exec chmod 660 {} \; Globbing: ls **/*.rb mv image?.{png,gif} images Tilde expansion: ls ~/src/ ls ~mark Reading and setting environment variables: echo $PATH echo abx${HOME}xyz export HELLO="Yo man!" Simple arithmetic (lines starting with a digit are eval-ed as Ruby): 17 * 123 Command expansion: ps `pgrep urchin` Job control (uses fg, bg and jobs builtins).
This gives an exception:
$ git co -b =
Urchin exited.
NoMethodError: undefined method `<<' for nil:NilClass
/home/mark/src/urchin/lib/parser.rb:195:in `arg_with_equals'
/home/mark/src/urchin/lib/parser.rb:281:in `words'
/home/mark/src/urchin/lib/parser.rb:142:in `parse_command'
/home/mark/src/urchin/lib/parser.rb:60:in `parse_job'
/home/mark/src/urchin/lib/parser.rb:28:in `jobs_from'
/home/mark/src/urchin/lib/shell.rb:59:in `parse_and_run'
/home/mark/src/urchin/lib/shell.rb:35:in `run'
/home/mark/src/urchin/bin/urchin:41:in `<main>'
Something I've often thought would be useful, when using commands I'm not intimately familiar with (or which have a bazillion options) is for the shell to be able to give feedback before I enter a command.
The most obvious, and probably most common, use for this would be giving information about command switches/options. If I'm using wget
, for instance, if I type wget -m -k -p
and then trigger feedback (either by a long pause, or a keystroke--whatever I've configured in .urchin_profile or whatever) I could get a brief one-line explanation of the last switch I entered, telling me that that causes wget to include related files like images and stylesheets and scripts related to the page being downloaded. This would be MUCH faster and easier for a user than opening a new terminal window and digging through a gigantic man page, or Googling, or testing the command and trying something else if it didn't work. Imagine if you had to lookup 10 different switches, which you could easily do with wget. Each might take a few seconds to try with this method, but might take a minute and a half each by Googling. That's 30 seconds versus 15 minutes. (The real value of speeding things up and smoothing them out is that you don't lose concentration, which is much more valuable than the time saved!)
Another example of how this could be used would be when sending commands to an ssh client. (Not ssh shell commands, but the commands you give the client after you type ~
to tell the client that what follows is not to be sent on the the machine you're connected to, but interpreted by the client itself.) Take a look at this question: http://serverfault.com/questions/435256/how-can-one-send-commands-to-the-inner-ssh-session/435262#435262 and imagine that you get feedback everytime you type ~
to tell you which machine's ssh client is going to receive the command.
What Urchin would provide would simply be a way of creating methods for intercepting and responding to incomplete commands, and a way of triggering the feedback. It would be a lot like command completion in bash or zsh in that some commands ship with the completions, and other times the user needs to write them, or find them on the web somewhere and install them. Urchin might initially include feedback methods for switches on a handful of commands, mostly as examples of how to write them, and to let people use them and see how they can be useful.
At some point some ambitious soul might do some metaprogramming to generate these feedback methods on the fly by parsing man pages. But that wouldn't be in the scope of the initial goals for this feature. Initially it's just about giving users a way to respond to not-yet-executed commands.
Perhaps like this (super-rough, off-the-top-of-my-head API example, probably terrible):
on_feedback_request_for /wget*-k/ do
display_tip "Convert links for offline viewing"
end
on_feedback_request_for /rm -rf*/ do
3.times { beep }
display_tip "Whoooa! Dude! Think carefully!", { :box_color => :red, :text => yellow }
do
As far as I know this goes beyond what any other shell can do, and offers possibilities for interaction that don't exist in those other shells. (Or, if they are possible in the other shells, they almost certainly aren't easy to implement and certainly haven't been extensively taken advantage of. Zsh and bash command completion might possibly be capable of doing something like this, but not so easily.)
I can see this really making Urchin much more productive even for experienced shell users, as well as much more approachable for beginners.
Since MRI 1.9, a pipe has been used for internal signalling communication. A C API, rb_reserved_fd_p()
, has been provided to check if a file descriptor is used by the VM or not.
Currently, Urchin closes all FDs > 2. For some reason, I haven't found this to be a problem under 1.9, but testing with Ruby 2.0.0-dev does give intermittent problems:
[ASYNC BUG] thread_timer: select
EBADF
ruby 2.0.0dev (2012-11-01 trunk 37411) [x86_64-linux]
Urchin should use the C API, either with a C extension or using FFI.
Although Bash has the ability to make Mutt go crazy too, it certainly seems to happen more in Urchin.
It's especially evident when resizing windows and the like. Something to do with terminal modes, I'd guess.
Perhaps this will be helped by issue #4.
$ touch "/home/mark/one two three"
$ touch "/home/mark/one two zero"
$ ls ~/on<TAB>
one two three one two zero
$ ls ~/one\ two\ <-- Correctly completes first two words - <TAB> again doesn't work.
Better!
They seem to go to the git Completion/git Command. Investigation required.
Every piece of info tracked as part of history is basically another field, and the more flexibility offered to the user (in terms of what to include and what to exclude from history) the more complicated it gets to the developer of the shell, leaving them to basically build database functions from scratch, when they would more easily just use a database (probably SQLite).
Here are some of the fields that might be tracked as part of history:
The killer feature to this, more than ease of development, would be the ability for the end user to define their own fields. For instance, if the working_directory field were defined by an end user, they would just have to specify the name (whatever name they wanted) and the command or value to run or evaluate on each command execution. In this case pwd
or $PWD would both do nicely. Allowing the user to add arbitrary data to their command history would make this the most flexible and powerful command history EVAR. Or at least I'd bet $5 on that.
Allow some method of excluding commands from the history - explicit list of command, regex, etc... who knows.
The most obvious example is, of course:
rm -rf .
For example, this works in bash, but not Urchin:
git commit -m'message'
Both instances occasionally try to create /dev/shm/urchin/, but there is a race condition.
Useful for .env_example
and the like.
Currently, a command like:
ls |<return>
will actually execute, rather than accept the next thing in the pipeline. Additionally, trying to enter inline Ruby on more than one line will raise an exception.
Making the parser multi-line aware is also the first step in being able to parse (nice) function definitions.
I tried building & installing the gem, (gem build urchin.gemspec, gem install urchin-0.1.0.90.gem) but when I run it, I get the following on a mac running 1.9.3 under rvm. Did I miss a step? I wasn't able to find any specific install instructions, and this does not seem to be in the gem repositories
looks like a great tool/eager to get it working!
Urchin exited.
LoadError: cannot load such file -- /usr/local/rvm/gems/ruby-1.9.3-p194/gems/urchin-0.1.0.90/bin/../boot
/usr/local/rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in require' /usr/local/rvm/rubies/ruby-1.9.3-p194/lib/ruby/site_ruby/1.9.1/rubygems/custom_require.rb:36:in
require'
/usr/local/rvm/gems/ruby-1.9.3-p194/gems/urchin-0.1.0.90/bin/urchin:15:in <top (required)>' /usr/local/rvm/gems/ruby-1.9.3-p194/bin/urchin:19:in
load'
/usr/local/rvm/gems/ruby-1.9.3-p194/bin/urchin:19:in <main>' /usr/local/rvm/gems/ruby-1.9.3-p194/bin/ruby_noexec_wrapper:14:in
eval'
/usr/local/rvm/gems/ruby-1.9.3-p194/bin/ruby_noexec_wrapper:14:in <main>' ^C/usr/local/rvm/gems/ruby-1.9.3-p194/gems/urchin-0.1.0.90/bin/urchin:54:in
gets': Interrupt
from /usr/local/rvm/gems/ruby-1.9.3-p194/gems/urchin-0.1.0.90/bin/urchin:54:in gets' from /usr/local/rvm/gems/ruby-1.9.3-p194/gems/urchin-0.1.0.90/bin/urchin:54:in
rescue in <top (required)>'
from /usr/local/rvm/gems/ruby-1.9.3-p194/gems/urchin-0.1.0.90/bin/urchin:6:in <top (required)>' from /usr/local/rvm/gems/ruby-1.9.3-p194/bin/urchin:19:in
load'
from /usr/local/rvm/gems/ruby-1.9.3-p194/bin/urchin:19:in <main>' from /usr/local/rvm/gems/ruby-1.9.3-p194/bin/ruby_noexec_wrapper:14:in
eval'
from /usr/local/rvm/gems/ruby-1.9.3-p194/bin/ruby_noexec_wrapper:14:in `
For example:
$ jruby --properties > ~/.jrubyrc
/home/mark/src/urchin/lib/os_process.rb:20:in `reopen': No such file or directory - ~/.jrubyrc (Errno::ENOENT)
from /home/mark/src/urchin/lib/os_process.rb:20:in `block in perform_redirects'
from /home/mark/src/urchin/lib/os_process.rb:16:in `each'
from /home/mark/src/urchin/lib/os_process.rb:16:in `perform_redirects'
from /home/mark/src/urchin/lib/os_process.rb:26:in `execute'
from /home/mark/src/urchin/lib/job.rb:77:in `block in fork_and_exec'
from /home/mark/src/urchin/lib/job.rb:55:in `fork'
from /home/mark/src/urchin/lib/job.rb:55:in `fork_and_exec'
from /home/mark/src/urchin/lib/job.rb:106:in `block in run'
from /home/mark/src/urchin/lib/job.rb:97:in `each'
from /home/mark/src/urchin/lib/job.rb:97:in `each_with_index'
from /home/mark/src/urchin/lib/job.rb:97:in `run'
from /home/mark/src/urchin/lib/shell.rb:66:in `block in parse_and_run'
from /home/mark/src/urchin/lib/shell.rb:62:in `each'
from /home/mark/src/urchin/lib/shell.rb:62:in `parse_and_run'
from /home/mark/src/urchin/lib/shell.rb:35:in `run'
from /home/mark/src/urchin/bin/urchin:41:in `<main>'
This will be bad in the future!
Allows editing a command line in $EDITOR.
I'm not entirely what the syntax for specifying this should be. Perhaps something like:
cd_on_entry do
rake db:migrate
rails server
end
Instead of the mess that is class instance variables. daa2f70 should allow this easily now.
^^^
Make Ruby a first-class citizen on the command line, basically.
For example:
touch "/tmp/hello Spakman"
ls /tmp/hel<tab><tab> --> ls /tmp/hello Spakman
(it should produce --> ls /tmp/hello\ Spakman)
I picture this being useful once multi-line commands are working.
I have duplicated work by implementing my own. Better to use the Readline implementation.
When looking back through command history (not simply to repeat a recent command, but when excavating deeper into the past to figure out how you solved a problem that you now face again) it is often useful to know where you were when you executed a command. This would be much easier to reconstruct if the current working directory was available for each command, rather than only available by looking at cd commands previously made.
This could be implemented by itself, or it could be done with the issue "Track shell history in a database", which would (I think) make it much easier to implement, and make changes and configurations to history tracking cakewalk, since every piece of info tracked as part of history is basically another field, and the more flexibility offered to the user (in terms of what to include and what to exclude from history) the more complicated it gets to the developer of the shell, leaving them to basically build database functions from scratch, when they would more easily just use a database. More details in that issue.
I can't think of a reason other than arithmetic to start a command line with a -.
Example:
cal aug
should be able to run:
cal 8 $year_of_the_next_august
It's irritating to forget to run a command using time and later want to know how long it took. Consider storing basic information about jobs and commands.
I'm not sure how best to implement this - POSIX extended attributes? Or a simple array of directories with corresponding prompts in .urchin.rb.
For example:
$ man ls &
$ man cp &
$ fg
Will have man ls
as the foreground job rather than man cp
.
Currently, a glob that doesn't match anything is simply ignored.
For example:
ls *.nothing --> ls
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.