SkillAgentSearch skills...

Daudin

A Python command-line shell

Install / Use

/learn @terrycojones/Daudin
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

Daudin - a Python command-line shell

daudin is a UNIX command-line shell based on Python.

The aim is to provide an interactive shell that is as convenient to use as the regular shell (in particular providing pipelines) but which has Python as its programming language.

Contents: <a href="#installation">Installation</a> · <a href="#usage">Usage</a> · <a href="#examples">Examples</a> · <a href="#pipelines">Pipelines</a> · <a href="#cd">Changing directory</a> · <a href="#command-substitution">Command substitution</a> · <a href="#readline">Readline</a> · <a href="#init-file">Init file</a> · <a href="#prompts">Prompts</a> · <a href="#more-usage">More on usage</a> · <a href="#exiting">Exiting</a> · <a href="#command-interpretation">Command interpretation</a> · <a href="#pipeline-python-execution">Pipeline execution</a> · <a href="#shell-execution">Shell execution</a> · <a href="#debugging">Debugging</s> · <a href="#version">Version</a> · <a href="#background">Background</a> · <a href="#todo">TODO</a>.

<a id="installation"></a>

Installation

$ pip install daudin

<a id="usage"></a>

Usage

Run daudin and enter commands interactively.

Should run fine on a recent version of Python 3 (I am using 3.7.3).

<a id="examples"></a>

Examples

The following examples all assume you have already run daudin (which prints the >>> prompt).

Like a regular shell, you have direct access to UNIX tools:

>>> ls -l
total 44
-rw-r--r-- 1 terry terry   635 Oct 12 17:34 Makefile
-rw-rw-r-- 1 terry terry 16619 Oct 12 23:05 README.md
-rwxrwxr-x 1 terry terry  1261 Oct 12 22:42 daudin
drwxrwxr-x 3 terry terry  4096 Oct 12 22:51 daudinlib
-rw-rw-r-- 1 terry terry  2309 Oct 12 23:05 example-functions.py
-rw-r--r-- 1 terry terry  1546 Oct 12 17:43 setup.py
drwxrwxr-x 3 terry terry  4096 Oct 12 22:48 test
>>> ls | wc -l
7
>>> echo hello there > /tmp/xxx
>>> cat /tmp/xxx
hello there

(If regular shell commands are slow to execute, it's probably because your regular shell is slow to start. Change the default underlying shell used by daudin to something faster, as described <a href="#shell-execution">below</a>.)

But in fact it's Python all the way down:

>>> from math import pi
>>> pi
3.141592653589793
>>> def area(r):
...   return r ** 2 * pi
...
>>> area(2.0)
12.566370614359172

<a id="pipelines"></a>

Pipelines

Shell pipelines are super cool. As you've seen above daudin, has pipelines that look just like the shell. But there are a few added extras.

You can mix Python and the shell in a daudin pipeline:

>>> import this | grep 'better than'
Beautiful is better than ugly.
Explicit is better than implicit.
Simple is better than complex.
Complex is better than complicated.
Flat is better than nested.
Sparse is better than dense.
Now is better than never.
Although never is often better than *right* now.

In Python commands in a daudin pipeline, the current pipeline value is kept in a variable named _, which may be of any type:

>>> -6 | abs(_) | _ * 7
42
>>> 'hello' | _.title()
Hello

UNIX commands produce lists of strings in _:

>>> ls | for name in _:
...   prefix = name.split('.')[0]
...   print(len(prefix), prefix.upper())
...
8 MAKEFILE
6 README
6 DAUDIN
9 DAUDINLIB
17 EXAMPLE-FUNCTIONS
5 SETUP
4 TEST

That means you should use _[0] if you want to act on just the first line of UNIX command output:

>>> def triple(x):
...   return int(x) * 3
...
>>> echo a b c | wc -w | triple(_[0])
9

Here's the same thing, but with cat reading from the terminal:

>>> def triple(x):
...   return int(x) * 3
...
>>> cat | wc -w | triple(_[0])
a b c
^D
9

You can really mix things up (UNIX, Python, UNIX, Python):

>>> seq 0 9 | map(lambda x: 2 ** int(x), _) | sum(_)
1023
>>> seq 0 9 | list(map(lambda x: 2 ** int(x), _)) | tee /tmp/powers-of-two | sum(map(int, _))
1023
>>> cat /tmp/powers-of-two
1
2
4
8
16
32
64
128
256
512

You can hit ENTER in the middle of a pipeline without disrupting it. So this:

>>> echo a b c | wc -w
3

is equivalent to this

>>> echo a b c |
>>> wc -w
3

And if you forget to end a pipeline command-line with a | you can just put one at the start of the next line to continue the pipeline:

>>> echo a b c
a b c
>>> | wc -w
3

If you need to write a helper function in the middle of the pipeline before continuing processing, you can do it:

>>> def triple(x):
...   # Doesn't use int() to convert its argument.
...   return x * 3
...
>>> echo a b c | wc -w
3
>>> f = lambda line: int(line[0])
>>> | f(_) | triple(_)
9

Similarly, you can change directories in the middle of a pipeline (see <a href="#cd">Changing directory</a> below).

You can put comments into the middle of a pipeline

>>> ls -1
Makefile
README.md
daudin
daudinlib
setup.py
test
>>> # The pipeline is still alive!
>>> _
['Makefile', 'README.md', 'daudin', 'daudinlib', 'setup.py', 'test']
>>> | wc -l
6

You can also pipe the output of a multi-line Python command directly into a following command:

>>> ls | for name in _:
...   prefix = name.split('.')[0]
...   print(len(prefix), prefix.upper()) | | sort
17 EXAMPLE-FUNCTIONS
4 TEST
5 SETUP
6 DAUDIN
6 README
7 LICENSE
8 MAKEFILE
9 CHANGELOG
9 DAUDINLIB

There are two | symbols before the sort above because an empty command is necessary to terminate the compound Python command.

The output above should have been numerically sorted. The pipeline can be immediately continued using a leading |:

>>> | sort -n
4 TEST
5 SETUP
6 DAUDIN
6 README
7 LICENSE
8 MAKEFILE
9 CHANGELOG
9 DAUDINLIB
17 EXAMPLE-FUNCTIONS

Here's another example where two | symbols can be used to terminate the Python command in order to continue the pipeline on a single line:

>>> ls | for i in _: print(i[:3]) | | wc -l
11

The above could instead be piped into the sus function I have in my ~/.daudin.py file (see <a href="#init-file">Init file</a> for details on this). The sus Python function does the equivalent of the shell sort | uniq -c | sort -nr trick for finding the most common inputs:

>>> ls | for i in _: print(i[:3]) | | sus()
3 dau
1 CHA
1 LIC
1 Mak
1 REA
1 dis
1 exa
1 set
1 tes

Just to repeat: the || provides an empty command (the zero-length string between the pipe symbols) to terminate the Python for block. The space between the two pipe symbols is optional.

Undo in a pipeline

If you run a command that alters the pipeline content and you want to restore it to its former value, you can undo with %u.

There is only a single undo at the moment. This could obviously be improved, and a redo command could be added.

You can of course always save the current pipeline value into a Python variable:

>>> echo a b c
>>> a = _

<a id="cd"></a>

Changing directory

Changing directory has to be handled a little specially because although a cd command can be handed off to a shell, the change of directory in that shell will have no effect on your current process. That's why cd has to be a special "built-in" command in regular shells.

In daudin you can change dir using regular Python:

>>> import os
>>> os.chdir('/tmp')
>>> pwd
/tmp

but that's far too laborious for interactive use. So there's a cd function provided for you:

>>> cd('/tmp')
>>> pwd
/tmp

To ease this, at the price of a litle ugliness, there's also a "built-in" special command called %cd:

>>> %cd /tmp
>>> pwd
/tmp

Changing directory does not affect the current pipeline value. So you can change directory in the middle of a pipeline:

>>> mkdir /tmp/a /tmp/b
>>> %cd /tmp/a
>>> touch x y z
>>> ls | %cd /tmp/b | for i in _:
...   with open(i + '.txt', 'w') as fp:
...     print('I am file', i, file=fp)
>>> pwd
/tmp/b
>>> cat x.txt
I am file x

<a id="command-substitution"></a>

Command substitution

In regular shells there is a way to have part of a command line executed in a sub-shell and the output of that sub-shell replaces that part of the original command.

For example, suppose you want to get the value of date into a variable. In the bash shell you could do this:

$ d=$(date)
$ echo $d
Sat Oct  5 22:36:24 CEST 2019

In daudin there is a sh function that you can use to pass commands to a sub-shell. So, equivalently:

>>> d = sh('date')
>>> d
Sat Oct  5 22:36:24 CEST 2019

If you then wanted to extract the month from the date output, in the shell you could do this:

# Working from the d variable set above:
$ month=$(echo $d | cut -f2 -d' ')
# Or you could call date again:
$ month=$(date | cut -f2 -d' ')
$ echo $month
Oct

Same thing in daudin:

# Working from the d variable set above:
>>> month = d.split()[1]
# Calling date again:
>>> month = sh('date').split()[1]
>>> month
Oct

You could also have the shell do all the work via sh() but to do that you'll need to use \| in the to ensure that daudin doesn't incorrectly split the shell command into two pieces:

>>> month = sh('date \| cut -f2 -d" "')

<a id="readline"></a>

Readline

daudin uses the GNU readline library to make it easy to edit and re-enter commands. The history is stored in ~/.daudin_history.

daudin provides file and directory name completion, as well as Python completion (the latter using rlcompleter).

<a id="init-file"></a>

Init file

daudin will initially read and execute code in a `~/.dau

View on GitHub
GitHub Stars179
CategoryDevelopment
Updated4mo ago
Forks9

Languages

Python

Security Score

92/100

Audited on Nov 18, 2025

No findings