Plumbum: Shell Combinators
Clone or download
smheidrich and henryiii Towards pathlib.Path.mkdir compatibility (#410)
* Implement mkdir args: parents, exist_ok

* Add tests for mkdir (local + remote)
Latest commit 361db26 Oct 16, 2018
Failed to load latest commit information.
.ci Fixing after Python brew change on macOS Apr 3, 2018
conda.recipe Paramiko 2.4 drops support for Python 2.6 Nov 21, 2017
docs Adding news release Aug 10, 2018
examples Adding better color display, example image Dec 28, 2016
experiments Rename encoding -> custom_encoding Dec 27, 2016
plumbum Towards pathlib.Path.mkdir compatibility (#410) Oct 16, 2018
tests Towards pathlib.Path.mkdir compatibility (#410) Oct 16, 2018
.appveyor.yml Appveyor fixes (#402) Aug 10, 2018
.coveragerc removing a few unneeded parts from coveralls Dec 28, 2016
.editorconfig Added .editorconfig Dec 6, 2017
.gitattributes fixes to chown. closes #34 Oct 6, 2012
.gitignore WIP: Better interaction with MyPy (#343) Aug 28, 2017
.pep8 pep8 changes Jun 2, 2013
.travis.yml div by 0 error Sep 24, 2018
CHANGELOG.rst Adding docs for release Aug 8, 2018
CONTRIBUTING.rst Update script for easier contributions Aug 26, 2017
LICENSE update changelog Jun 2, 2013 Adding translations to manifest Nov 27, 2017
Pipfile Adding marker to ensure Paramiko is not installed on Python 2.6 Apr 9, 2018
README.rst Adding Conda badge Nov 28, 2017 Adding news release Aug 10, 2018
dev-requirements.txt Fix file handle leak in NOHUP / posix_daemonize (#418) Oct 16, 2018
pytest.ini BUGFIX: flush was not being called for terminal actions. Nov 18, 2015
setup.cfg Version bump Aug 8, 2018 Adding 3.7 support Jul 7, 2018 Rendering German translation Nov 2, 2017


Documentation Status Linux and Mac Build Status Windows Build Status Coverage Status PyPI Status PyPI Versions Anaconda-Server Badge PyPI License Join the chat at

Plumbum: Shell Combinators

Ever wished the compactness of shell scripts be put into a real programming language? Say hello to Plumbum Shell Combinators. Plumbum (Latin for lead, which was used to create pipes back in the day) is a small yet feature-rich library for shell script-like programs in Python. The motto of the library is "Never write shell scripts again", and thus it attempts to mimic the shell syntax ("shell combinators") where it makes sense, while keeping it all Pythonic and cross-platform.

Apart from shell-like syntax and handy shortcuts, the library provides local and remote command execution (over SSH), local and remote file-system paths, easy working-directory and environment manipulation, and a programmatic Command-Line Interface (CLI) application toolkit. Now let's see some code!

This is only a teaser; the full documentation can be found at Read the Docs

Cheat Sheet


>>> from plumbum import local
>>> ls = local["ls"]
>>> ls
LocalCommand(<LocalPath /bin/ls>)
>>> ls()
>>> notepad = local["c:\\windows\\notepad.exe"]
>>> notepad()                                   # Notepad window pops up
u''                                             # Notepad window is closed by user, command returns

Instead of writing xxx = local["xxx"] for every program you wish to use, you can also import commands

>>> from plumbum.cmd import grep, wc, cat, head
>>> grep
LocalCommand(<LocalPath /bin/grep>)


>>> chain = ls["-a"] | grep["-v", "\\.py"] | wc["-l"]
>>> print chain
/bin/ls -a | /bin/grep -v '\.py' | /usr/bin/wc -l
>>> chain()


>>> ((cat < "") | head["-n", 4])()
u'#!/usr/bin/env python\nimport os\n\ntry:\n'
>>> (ls["-a"] > "file.list")()
>>> (cat["file.list"] | wc["-l"])()

Working-directory manipulation

>>> local.cwd
<Workdir /home/tomer/workspace/plumbum>
>>> with local.cwd(local.cwd / "docs"):
...     chain()

Foreground and background execution

>>> from plumbum import FG, BG
>>> (ls["-a"] | grep["\\.py"]) & FG         # The output is printed to stdout directly
>>> (ls["-a"] | grep["\\.py"]) & BG         # The process runs "in the background"
<Future ['/bin/grep', '\\.py'] (running)>

Command nesting

>>> from plumbum.cmd import sudo
>>> print sudo[ifconfig["-a"]]
/usr/bin/sudo /sbin/ifconfig -a
>>> (sudo[ifconfig["-a"]] | grep["-i", "loop"]) & FG
lo        Link encap:Local Loopback
          UP LOOPBACK RUNNING  MTU:16436  Metric:1

Remote commands (over SSH)

Supports openSSH-compatible clients, PuTTY (on Windows) and Paramiko (a pure-Python implementation of SSH2)

>>> from plumbum import SshMachine
>>> remote = SshMachine("somehost", user = "john", keyfile = "/path/to/idrsa")
>>> r_ls = remote["ls"]
>>> with remote.cwd("/lib"):
...     (r_ls | grep[""])()

CLI applications

import logging
from plumbum import cli

class MyCompiler(cli.Application):
    verbose = cli.Flag(["-v", "--verbose"], help = "Enable verbose mode")
    include_dirs = cli.SwitchAttr("-I", list = True, help = "Specify include directories")

    @cli.switch("--loglevel", int)
    def set_log_level(self, level):
        """Sets the log-level of the logger"""

    def main(self, *srcfiles):
        print "Verbose:", self.verbose
        print "Include dirs:", self.include_dirs
        print "Compiling:", srcfiles

if __name__ == "__main__":
Sample output
$ python -v -I foo/bar -Ispam/eggs x.cpp y.cpp z.cpp
Verbose: True
Include dirs: ['foo/bar', 'spam/eggs']
Compiling: ('x.cpp', 'y.cpp', 'z.cpp')

Colors and Styles

from plumbum import colors
    print("This library provides safe, flexible color access.")
    print(colors.bold | "(and styles in general)", "are easy!")
print("The simple 16 colors or",
      colors.orchid & colors.underline | '256 named colors,',
      colors.rgb(18, 146, 64) | "or full rgb colors",
      'can be used.')
print("Unsafe " + + "color access" + + " is available too.")