sh
bash in Python.
Nir Cohen @ Gigaspaces
@thinkops
Why sh?
- os.system()? Featureless!
- More human readable than subprocess
- Much less demanding than Fabric local()
- More Pythonic than Plumbum...
- Automatic executable mapping.
- Wonderfully documented (one pager! damn!).
- Mucho Fun to use.
Subprocess
import subprocess
import time
def run_command(cmd):
print('Executing: {0}...'.format(cmd))
pipe = subprocess.PIPE
proc = subprocess.Popen(cmd, shell=True, stdout=pipe, stderr=pipe)
proc.aggr_stdout = ''
# while the process is still running, print output
while proc.poll() is None:
output = proc.stdout.readline()
proc.aggr_stdout += output
if len(output) > 0:
print(output)
time.sleep(0.2)
output = proc.stdout.readline()
proc.aggr_stdout += output
if len(output) > 0:
print(output)
proc.aggr_stderr = proc.stderr.read()
if len(proc.aggr_stderr) > 0:
print(proc.aggr_stderr)
return proc
output = run_command('pip install repex')
if output.returncode == 0:
print('HOORAY!')
else:
print('Docker.')
Fabric (local)
import fabric.api as fab
def run_command(cmd):
print('Executing: {0}...'.format(cmd))
# oi. either it'll print or return in proc.stdout.
# Not both..
proc = fab.local(cmd, capture=False)
return proc
output = run_command('pip install repex')
if output.succeeded:
print('HOORAY!')
else:
print('Docker.')
nicer!
import sh
o = sh.pip.install('repex')
print o
if o.exit_code == 0:
print('HOORAY!')
else:
print('Docker.')
execute. (with args)
# virtualenv test -p /usr/bin/python
import sh
vpath = 'test'
python = '/usr/bin/python'
# execute with arguments
sh.virtualenv(vpath, '-p', python)
# or comfortably named arguments
sh.virtualenv(vpath, p=python)
premade exec object
# test/bin/pip install python-packer==0.0.1 --default-timeout 60 &
env["PIP_DEFAULT_TIMEOUT"] = "60"
bins_dir = 'scripts' if sys.platform == 'win32' else 'bin'
pip = sh.Command(os.path.join('test', bins_dir, 'pip'))
# You can iterate over the output, pass env, acceptable exit
# codes and put a process in the background while
# printing its output.
for line in pip.install(
'python-packer==0.0.1',_iter=True, _env=env,
_ok_code=[0,19], _bg=True):
print line
advanced processing
# test/bin/pip install repex==0.8 >> output.file
# test/bin/pip install feedr 1> output.file
def _process(line):
print(line)
# output can be directed to files
try:
pip.install('repex==0.8', _out='output.file')
except sh.ErrorReturnCode_1 as ex:
print('Could not install: {0}'.format('repex==0.8'))
# or file objects.
with open('output.file', 'w') as f:
process = pip.install(
'feedr', _out=f, _err=_process, _bg=True)
# backgrounds processes can be waited upon before acting.
process.wait()
...
piping
# test/bin/pip freeze | grep -v python-packer | wc -l
# you can use the output
modules = pip.freeze().splitlines()
print 'YAY!' if 'python-packer==0.0.1' in modules \
else 'DOCKER!'
# or you can pipe..
i = sh.wc(sh.grep(
pip.freeze(), '-v', 'python-packer'), '-l'))
print('number of modules installed which '
'are not python-packer: {0}'.format(i)
bake a cake
# pip wheel python-packer --verbose --find-links==dir
# mkdir -p my_wheels_dir
# mv dir/*.whl my_wheels_dir
from sh import glob as g
wheel = pip.wheel
# bake arguments for reuse
wheel = wheel.bake('--verbose', '--find-links==dir')
# now every `wheel` command will run with
# `--verbose` and `--find-links`
wheel('feedr', _out=_process)
wheel('repex', _out=_process)
sh.mkdir('-p', 'my_wheels_dir')
# glob expansion
sh.mv(g('dir/*.whl'), 'my_wheels_dir')
context, advanced piping
# test/bin/mouth feed -t=File -f=ApacheAccess --gap=1 &
# sleep 2
# tail -f generated.log | grep GET
feedr = sh.Command('test/bin/mouth')
# run my program in the background and detach from shell
with sh.nohup:
x = feedr.feed(
t='File', t='ApacheAccess', g='1', _bg=True)
do_something_with(x)
time.sleep(2)
if os.path.isfile('generated.log'):
# pass stdout from a long running process by piping
# its output while it runs
sh.grep(sh.tail('-f', 'generated.log', _piped=True),
'GET', _out=_process)
MOAR!
-
stdin processing
-
stdin interactive callbacks
-
stdin/stdout buffer size control
https://amoffat.github.io/sh/
sh
By Nir Cohen
sh
- 2,181