Quick Links
Tomer Filiba Tomer Filiba
Plumbum: Shell Combinators and More¶
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, quick access to ANSI colors, and a programmatic Command-Line Interface (CLI) application toolkit. Now let’s see some code!
News¶
2026年06月03日: Version 2.0.0 released with async, static typing, more pathlib API, no pywin32 dep, and much more!
2025年10月31日: Version 1.10.0 released with Python 3.9-3.14 support, and some small fixes.
2024年10月05日: Version 1.9.0 released with Python 3.8-3.13 support, and some small fixes.
2024年04月29日: Version 1.8.3 released with some small fixes, final version to support Python 3.6 and 3.7.
2023年05月30日: Version 1.8.2 released with a PyPI metadata fix, Python 3.12b1 testing, and a bit more typing.
Cheat Sheet¶
Basics¶
>>> fromplumbumimport local >>> ls = local["ls"] >>> ls LocalCommand(<LocalPath /bin/ls>) >>> ls() 'build.py\ndist\ndocs\nLICENSE\nplumbum\nREADME.rst\nsetup.py\ntests\ntodo.txt\n' >>> notepad = local["c:\\windows\\notepad.exe"] >>> notepad() # Notepad window pops up '' # 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:
>>> fromplumbum.cmdimport grep, wc, cat, head >>> grep LocalCommand(<LocalPath /bin/grep>)
Or, use the local.cmd syntactic-sugar:
>>> local.cmd.ls LocalCommand(<LocalPath /bin/ls>) >>> local.cmd.ls() 'build.py\ndist\ndocs\nLICENSE\nplumbum\nREADME.rst\nsetup.py\ntests\ntodo.txt\n'
See Local Commands.
Piping¶
>>> chain = ls["-a"] | grep["-v", "\\.py"] | wc["-l"] >>> print(chain) /bin/ls -a | /bin/grep -v '\.py' | /usr/bin/wc -l >>> chain() '13\n'
See Pipelining.
Redirection¶
>>> ((cat < "setup.py") | head["-n", 4])() '#!/usr/bin/env python3\nimport os\n\ntry:\n' >>> (ls["-a"] > "file.list")() '' >>> (cat["file.list"] | wc["-l"])() '17\n'
Working-directory manipulation¶
>>> local.cwd <Workdir /home/tomer/workspace/plumbum> >>> with local.cwd(local.cwd / "docs"): ... chain() ... '15\n'
A more explicit, and thread-safe way of running a command in a different directory is using the .with_cwd() method:
.. code-block:: python
>>> ls_in_docs = local.cmd.ls.with_cwd("docs") >>> ls_in_docs() 'api\nchangelog.rst\n_cheatsheet.rst\ncli.rst\ncolorlib.rst\n_color_list.html\ncolors.rst\nconf.py\nindex.rst\nlocal_commands.rst\nlocal_machine.rst\nmake.bat\nMakefile\n_news.rst\npaths.rst\nquickref.rst\nremote.rst\n_static\n_templates\ntyped_env.rst\nutils.rst\n'
See Paths and The Local Object.
Foreground and background execution¶
>>> fromplumbumimport FG, BG >>> (ls["-a"] | grep["\\.py"]) & FG # The output is printed to stdout directly build.py .pydevproject setup.py >>> (ls["-a"] | grep["\\.py"]) & BG # The process runs "in the background" <Future ['/bin/grep', '\\.py'] (running)>
Command nesting¶
>>> fromplumbum.cmdimport 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
See Command Nesting.
Remote commands (over SSH)¶
Supports openSSH-compatible clients, PuTTY (on Windows) and Paramiko (a pure-Python implementation of SSH2):
>>> fromplumbumimport SshMachine >>> remote = SshMachine("somehost", user = "john", keyfile = "/path/to/idrsa") >>> r_ls = remote["ls"] >>> with remote.cwd("/lib"): ... (r_ls | grep["0.so.0"])() ... 'libusb-1.0.so.0\nlibusb-1.0.so.0.0.0\n'
See Remote.
CLI applications¶
importlogging fromplumbumimport cli classMyCompiler(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) defset_log_level(self, level): """Sets the log-level of the logger""" logging.root.setLevel(level) defmain(self, *srcfiles): print("Verbose:", self.verbose) print("Include dirs:", self.include_dirs) print("Compiling:", srcfiles) if __name__ == "__main__": MyCompiler.run()
Sample output¶
$ python3 simple_cli.py -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¶
fromplumbumimport colors with colors.red: 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 " + colors.bg.dark_khaki + "color access" + colors.bg.reset + " is available too.")
Sample output¶
This library provides safe color access.
Color (and styles in general) are easy!
The simple 16 colors, 256 named colors, or full hex colors can be used.
Unsafe color access is available too.
See Colors.
Development and Installation¶
The library is developed on GitHub, and will happily accept patches from users. Please use the GitHub’s built-in issue tracker to report any problem you encounter or to request features. The library is released under the permissive MIT license.
Requirements¶
Plumbum supports CPython and PyPy and is continually tested on
Linux, Mac, and Windows machines through GitHub Actions. Any Unix-like machine
should work fine out of the box, but on Windows, you’ll probably want to
install a decent coreutils
environment and add it to your PATH, or use WSL(2). I can recommend mingw (which comes bundled with Git for Windows), but cygwin should
work too. If you only wish to use Plumbum as a Popen-replacement to run Windows
programs, then there’s no need for the Unix tools.
Note that for remote command execution, an openSSH-compatible client is
required (also bundled with Git for Windows), and a bash-compatible shell
and a coreutils environment is also expected on the host machine.
Download¶
You can download the library from the Python Package Index (in a variety of formats), or
run pip install plumbum directly. If you use Anaconda, you can also get it
from the conda-forge channel with conda install -c conda-forge plumbum.
User Guide¶
The user guide covers most of the features of Plumbum, with lots of code-snippets to get you swimming in no time. It introduces the concepts and "syntax" gradually, so it’s recommended you read it in order. A quick reference guide is available.
User Guide
Reference
API Reference¶
The API reference (generated from the docstrings within the library) covers all of the exposed APIs of the library. Note that some "advanced" features and some function parameters are missing from the guide, so you might want to consult with the API reference in these cases.
API Reference
- Package plumbum.cli
ApplicationShowCompletionShowHelpShowHelpAllShowVersionCountOfFlagMissingArgumentMissingMandatorySwitchPositionalArgumentsErrorPredicateRangeSetSubcommandErrorSwitchAttrSwitchCombinationErrorSwitchErrorSwitchInfoUnknownSwitchValidatorWrongArgumentTypeautoswitch()positionalswitch()- Terminal-related utilities
Progressask()choose()get_terminal_size()prompt()readline()- Terminal size utility
get_terminal_size()- Progress bar
ProgressProgressAutoProgressBaseProgressIPyConfigConfigBaseConfigINIShowImageApp
- Package plumbum.commands
AppendingStdoutRedirectionBaseCommandBaseRedirectionBoundCommandBoundEnvCommandConcreteCommandPipelineRedirectionErrorStderrRedirectionStdinDataRedirectionStdinRedirectionStdoutRedirectioniter_lines()run_proc()shquote()FuturePipeToLoggerMixinBGFGNOHUPRETCODETEETFCommandNotFoundModeProcessExecutionErrorProcessLineTimedOutProcessTimedOutiter_lines()run_proc()- Design Philosophy
- Why Delegation Instead of Inheritance?
- Example Usage
AsyncCommandAsyncLocalCommandAsyncPipelineProcessAsyncRemoteCommandAsyncResult
- Package plumbum.machines
- Package plumbum.path
- Package plumbum.fs
- Package plumbum.colors
- Colorlib design
Note
The local object is an instance of a machine.
About¶
The original purpose of Plumbum was to enable local and remote program execution with ease, assuming nothing fancier than good-old SSH. On top of this, a file-system abstraction layer was devised, so that working with local and remote files would be seamless.
I’ve toyed with this idea for some time now, but it wasn’t until I had to write build scripts
for a project I’ve been working on that I decided I’ve had it with shell scripts and it’s time
to make it happen. Plumbum was born from the scraps of the Path class, which I
wrote for the aforementioned build system, and the SshContext and SshTunnel classes
that I wrote for RPyC. When I combined the two with shell combinators
(because shell scripts do have an edge there) the magic happened and here we are.
Credits¶
The project has been inspired by PBS (now called sh) of Andrew Moffat, and has borrowed some of his ideas (namely treating programs like functions and the nice trick for importing commands). However, I felt there was too much magic going on in PBS, and that the syntax wasn’t what I had in mind when I came to write shell-like programs. I contacted Andrew about these issues, but he wanted to keep PBS this way. Other than that, the two libraries go in different directions, where Plumbum attempts to provide a more wholesome approach.
Plumbum also pays tribute to Rotem Yaari who suggested a
library code-named pyplatform for that very purpose, but which had never materialized.