https://github.com/benrutter/clamshell
Skip to content Toggle navigation
Sign up
* Product
+
Actions
Automate any workflow
+
Packages
Host and manage packages
+
Security
Find and fix vulnerabilities
+
Codespaces
Instant dev environments
+
Copilot
Write better code with AI
+
Code review
Manage code changes
+
Issues
Plan and track work
+
Discussions
Collaborate outside of code
+ Explore
+ All features
+ Documentation
+ GitHub Skills
+ Blog
* Solutions
+ For
+ Enterprise
+ Teams
+ Startups
+ Education
+ By Solution
+ CI/CD & Automation
+ DevOps
+ DevSecOps
+ Case Studies
+ Customer Stories
+ Resources
* Open Source
+
GitHub Sponsors
Fund open source developers
+
The ReadME Project
GitHub community articles
+ Repositories
+ Topics
+ Trending
+ Collections
* Pricing
[ ]
*
#
In this repository All GitHub |
Jump to |
* No suggested jump to results
*
#
In this repository All GitHub |
Jump to |
*
#
In this user All GitHub |
Jump to |
*
#
In this repository All GitHub |
Jump to |
Sign in
Sign up
{{ message }}
benrutter / clamshell Public
* Notifications
* Fork 0
* Star 27
experimenting with a python based shell
License
MIT license
27 stars 0 forks
Star
Notifications
* Code
* Issues 0
* Pull requests 0
* Actions
* Projects 0
* Security
* Insights
More
* Code
* Issues
* Pull requests
* Actions
* Projects
* Security
* Insights
benrutter/clamshell
This commit does not belong to any branch on this repository, and may
belong to a fork outside of the repository.
main
Switch branches/tags
[ ]
Branches Tags
Could not load branches
Nothing to show
{{ refName }} default View all branches
Could not load tags
Nothing to show
{{ refName }} default
View all tags
Name already in use
A tag already exists with the provided branch name. Many Git commands
accept both tag and branch names, so creating this branch may cause
unexpected behavior. Are you sure you want to create this branch?
Cancel Create
3 branches 0 tags
Code
* Local
* Codespaces
*
Clone
HTTPS GitHub CLI
[https://github.com/b]
Use Git or checkout with SVN using the web URL.
[gh repo clone benrut]
Work fast with our official CLI. Learn more.
* Open with GitHub Desktop
* Download ZIP
Sign In Required
Please sign in to use Codespaces.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching GitHub Desktop
If nothing happens, download GitHub Desktop and try again.
Launching Xcode
If nothing happens, download Xcode and try again.
Launching Visual Studio Code
Your codespace will open once ready.
There was a problem preparing your codespace, please try again.
Latest commit
@benrutter
benrutter Typo fix
...
f994044 Jan 28, 2023
Typo fix
f994044
Git stats
* 36 commits
Files
Permalink
Failed to load latest commit information.
Type
Name
Latest commit message
Commit time
clamshell
Merge pull request #2 from benrutter/hidden-file-support
Jan 28, 2023
.gitignore
"git ignore"
Jan 2, 2023
LICENSE
Initial commit
Dec 22, 2022
README.md
Typo fix
Jan 28, 2023
pyproject.toml
"hidden file support and readme"
Jan 28, 2023
requirements.txt
"hidden file support and readme"
Jan 28, 2023
View code
[ ]
clamshell What is this? That sounds like xonsh Like command line
arguments you say? A quick tour of built in functions goto: move
directories files: get a list of files copy and move delete pipe and
run Other functions clamrc.py super_commands aliases get_prompt &
get_continuation_prompt How do I get this wonderful beast!?
README.md
clamshell
Experimental shell for utopians!
What is this?
clamshell is a repl shell for using python as an interactive shell
(like bash, fish, zsh etc)
I love working in the terminal, and use Bash all the time, but
there's a bunch of stuff I feel like I wish it has:
* Autocompletion, syntax highlighting etc
* Actual types that got used (like arrays, numbers, ints)
* Easier syntax (does anyone actually know how to define a
function, loop through something, add two numbers without having
to look it up every time?)
* Less terse (This is the most subjective, but built in functions
like 'cd', 'ls' are designed to be quick to type, but we have
autocompletion for that- I'd rather things were more obvious to
beginners)
It seems odd to me that as developers, our daily diver is missing so
much of the functionality that we demand from our modern languages.
I built clamshell as an experiment to think about what an ideal shell
utopia might look like.
It's an experiment, and you can run it on windows, mac or linux.
That sounds like xonsh
Yes it does! Xonsh is actually a much better idea if you're looking
for something to use since it is production ready, has less
dependencies and a safer execution pattern check it out!
But, xonsh is based on the idea of using python with another existing
shell language like bash, I wanted to experiment with what it would
be like to actually have a fully python shell - where your standard
shell commands had types, and where python functions could get ran
just like command line arguments/
Like command line arguments you say?
Yeah! Let's taken an example.
Python has a function to change directory already, so why not just
load up a default python repl and do stuff like this:
os.chdir('Music/great-tunes/')
It should work fine, but it seems like a lot more typing than just:
cd Music/great-tunes
So clamshell adds an extra layer of interpretation whereby this:
os.chdir Music/great-tunes
gets ran as if it has the first command.
This works for anything you define as well, so say we type up this
new function:
def say_hello(name='stranger'):
print(f'Hello {name}!')
We can run this as usual with:
say_hello('Joe')
>> "Hello Joe!"
But you also have the option of running it with clam syntax:
say_hello Joe
>> "Hello Joe!"
There's also a concept of "super_commands" (sorry for the terrible
name), where a function explicitly set as one, will run when typed in
with no arguments (rather than just spitting back the type, as is
normal python repl behaviour):
say_hello
>> "Hello stranger!"
Pretty handy right?! If something isn't runnable as python, but is a
runnable command, then that'll work too:
docker run
>> "doing docker stuff"
You can import .py files and modules as you would in a normal repo
too:
import pandas as pd
And then use that in your interactive shell.
Bear in mind, this is all experimental, and you probably shouldn't
use it in production - again, see the awesome Xonsh if you want to
use python in the shell in a more robust setting.
A quick tour of built in functions
goto: move directories
Initial use is basically just like 'cd' in bash:
~/me $_ goto Documents/some_folder
~/me/Documents/some_folder $_
We move to working in another directory. So far so good!
Aside from 'goto' being a more beginner friendly name that 'cd', this
is pretty much identical behaviour.
But. . .
As a nice tweak, clamshell maps out your directories on start up, so
if a folder isn't hidden, and is within a certain depth from your
home directory, you don't have to type the full path:
~/me $_ goto cool_album
~/me/Music/cool_album $_ goto important_docs
~/me/Documents/important_docs $_
Nice! That's a lot less time we can spend remembering where we saved
a folder.
files: get a list of files
This is the equivalent of 'ls', although it returns a special "file
list" which is basically just a list that clamshell will print as a
nice table.
~/me $_ files
We can pass arguments to see another folder, include hidden files,
and recur down to see subfolders too
~/me $_ files Documents/another_folder True 3
That above command calls the equivalent of this:
files(path='Documents/another_folder', hidden=True, recursive=3)
which means it'll include hidden files and folders, and recur into
folders up to a depth of 3.
Because the return is a list, we can do things like loop through it's
return (although note that for setting variables, we no longer get to
use our fun clam syntax ):
~/me $_ tunes = files('Music')
~/me $_ for item in tunes:
> if 'Rick Astley' not in item['name']:
> delete(item['path'])
Phew! That deleted every file and folder in Music that didn't have
Rick Astley in the name (more on delete later)
copy and move
Not much to save about these, they copy or move a file/folder from
one place to another:
~/me $_ move some_file.txt a_folder/somewhere_else.txt
Although, combined with the files() return, we can do some cool stuff
to move things from one location to another:
~/me/Downloads/new_album $_ to_move = files()
~/me/Downlaods/new_album $_ goto Music
~/me/Music $_ [copy(i['path'], i['name']) for i in to_move]
delete
Works a bit like rm, except that it'll put things into the recycle
bin thanks to the beautiful send2trash module.
So we can do this in a burst of desire to destroy everything:
~/me $_ delete Documents
but unlike running:
~/me $_ rm -rf Documents
We can recover our stuff later.
pipe and run
Sadly, if we're in some python logic, we can't just run shell
commands. This function (which uses my 'mpy3' command line runnable)
won't work:
~/me/Music $_ for i in files():
> if i['name'].endswith('.mp3'):
> mpy3 i['name']
If something isn't native python, once we're in a loop for function,
we have to use "run" (which is basically just pythons subprocessing
module's run.
It returns an object with .stdout and .stderr methods that can be
handy, so you can set variables, or process standard output and input
pretty easy.
~/me/Music $_ for i in files():
> if i['name'].endswith('.mp3'):
> run(f'mpy3 {i['name']}')
There's also a handy "pipe" function, that'll do basically what you
think (pipe the results from the first argument into the second, and
the second into the third, etc).
~/me/Music $_ pipe(
> files()[0],
> lambda x: run(f'mpy3 {x}')
> )
or something like:
~/me/Music $_ pipe(
> run('ls')
> lambda x: print(f'output of ls is {x.stdout}')
> )
Other functions
There are a bunch of other functions to, which are a bit more self
explanatory, and have less to say on: - read(name_of_file) -> prints
out a nicely formatted version of a file - search(string, path,
recursive=0) -> will seach for a string occurence within files and
give use the lines - make_file, make_directory -> make a file or
directory with the name of the argument given
clamrc.py
on first running, clamshell with make a file in your home under
'.config/clamshell/clamrc.py'. This'll get ran and brought into
global variables every time you start up clamshell.
At first it'll just be blank.
It's just a normal python file, so if we add something like this to
it:
interesting_opinion = """
I'd just like to interject for a moment. What you're refering to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux. Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX.
"""
def be_interesting():
print(interesting_optinion)
We can call that function anytime in our repl:
~/me $_ be_interesting()
I'd just like to interject for a moment. What you're refering to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux. Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX.
There's a couple of special tweaks that are worth menthioning. . .
super_commands
Again, sorry for the name, but if we define a list with it of strings
of the names of functions (kinda wierd, sorry!). Then those functions
become 'super_commands', and are ran whenever they're typed without
having to use python's () function sytanx.
So if we add this to the end of our clamrc.py:
super_commands = ['be_interesting']
Now we can be interesting with even less keystrokes:
~/me $_ be_interesting
I'd just like to interject for a moment. What you're refering to as Linux, is in fact, GNU/Linux, or as I've recently taken to calling it, GNU plus Linux. Linux is not an operating system unto itself, but rather another free component of a fully functioning GNU system made useful by the GNU corelibs, shell utilities and vital system components comprising a full OS as defined by POSIX.
aliases
Just like with bash, we can alias things by adding them in our
clamrc.py.
We just add a dictionary called "aliases":
aliases = {'python2': 'python3'}
Now every time someone tries to run python2, python3 will run
instead. Phew, how helpful!
get_prompt & get_continuation_prompt
We can define a custom prompt by making a function that returns a
string:
import os
def get_prompt():
return 'cooooooooooooool ' + os.getcwd() + ' >'
def continuation_prompt():
return 'keep it up>>'
Now our shell looks like this:
coooooooool ~/me > def something():
keep it up>> print('stuff')
Aside from that, the whole file is just normal python that'll get
interpretted as normal python.
That makes it pretty easy to write quick utilities for ourselves, or
take use of python's capabilities to do something crazy, like:
* Print out an inspirational quote every time we start the shell
* Delete every file we own if it's 14:34
* Get the price of bitcoin from an api and print it out in the
command prompt
How do I get this wonderful beast!?
Though pip:
pip install clamshell
and then either:
clamshell
or
python -m clamshell
About
experimenting with a python based shell
Resources
Readme
License
MIT license
Stars
27 stars
Watchers
1 watching
Forks
0 forks
Releases
No releases published
Packages 0
No packages published
Used by 11
* @eyoo95
* @IFIF3526
* @raccoon-dog-p
* @jkong72
+ 3
Languages
* Python 100.0%
Footer
(c) 2023 GitHub, Inc.
Footer navigation
* Terms
* Privacy
* Security
* Status
* Docs
* Contact GitHub
* Pricing
* API
* Training
* Blog
* About
You can't perform that action at this time.
You signed in with another tab or window. Reload to refresh your
session. You signed out in another tab or window. Reload to refresh
your session.