Search
  • Jack Orenstein

Marcel the Shell

Welcome to marcel! Marcel is yet another shell that pipes objects between commands, instead of strings. What is different about marcel is it's reliance on Python. First, it is implemented in Python.


Second, marcel exposes Python. If you have an input stream of Files and you want to output a stream of (file name, file size) tuples, then you can use the map operator, which applies a function to input values to generate output values. The function used by the map operator is an ordinary Python function. So for example:

ls -fr | map (lambda file: (file.path, file.size))

ls -fr visits the current directly recursively (-r) and outputs files only (-f), not directories or symlinks. The function is in parentheses, (you could omit the lambda keyword if you'd like).


Third, marcel uses Python to take a different approach to scripting. Shell commands are augmented by control structures and rudimentary datatypes to provide a scripting language. These languages are usually pretty poorly designed, (e.g. bash). And of course, it's yet another language to learn. Furthermore, individual commands have extensive sub-languages, and to do anything sophisticated requires the reading of man pages documenting these sub-languages.


The marcel approach is to recognize that Python is a good programming language, but is bad at scripting because of the API to the host OS. You can't just run the ls command from Python, for example, you have to use something like subprocess.Popen. Piping requires chaining the stdin/stdout arguments together from successive Popen calls. Marcel offers an API module, marcel.api, which provides much cleaner integration. For example, the above example, done as Python code:

from marcel.api import *

for path, size in (ls(file=True, recursive=True) 
                   | map(lambda file: (file.path, file.size))):
    print(f'{file.path}: {file.size})

The ls -fr command, which was used in the shell, turns into ls(file=True, recursive=True) in the API. The pipe symbol again pipes the ls output to the map input. The command output is made available as a Python iterator, so that a for loop can iterate over the results.


38 views
  • github
  • Twitter
  • email