What is sh?
sh is a unique subprocess wrapper that maps your system programs to Python functions dynamically. sh helps you write shell scripts in Python by giving you the good features of Bash (easy command calling, easy piping) with all the power and flexibility of Python.
Starting with sh
sh is a full-fledged subprocess interface for Python that allows you to call any program as if it were a function. sh lets you call just about anything that you could run from a login shell much more neatly than you can with subprocess.Popen, and more importantly lets you capture and parse the output much more readily.
Installation
The installation of sh is done through the pip command $> pip install sh
Usage
The easiest way to get up and running is to import sh directly or import your program from sh. Every command you want to run is imported like any other module. That command is then usable just like a Python statement. Arguments are passed per usual, and output can be captured and worked with in a like fashion.
1
2
3
4
5
6
7
8
9
10
11
12
| # get interface information import sh print sh.ifconfig( "eth0" ) from sh import ifconfig print ifconfig( "eth0" ) # print the contents of this directory print ls( "-l" ) # substitute the dash for an underscore for commands that have dashes in their names |
Executing Commands with sh
Commands are called just like functions. "Note that these aren't Python functions, these are running the binary commands on your system dynamically by resolving your PATH, much like Bash does. In this way, all the programs on your system are easily available in Python."
Many programs have their own command subsets, like git (branch, checkout). sh handles subcommands through attribute access.
1
2
3
4
5
6
| from sh import git # resolves to "git branch -v" print (git.branch( "-v" )) print (git( "branch" , "-v" )) # the same command |
Keyword Arguments
Keyword arguments also work like you'd expect: they get replaced with the long-form and short-form commandline option. [source]
1
2
3
4
5
6
7
8
9
10
11
| # Resolves to "curl http://duckduckgo.com/ -o page.html --silent" # If you prefer not to use keyword arguments, this does the same thing # Resolves to "adduser amoffat --system --shell=/bin/bash --no-create-home" sh.adduser( "amoffat" , system = True , shell = "/bin/bash" , no_create_home = True ) # or sh.adduser( "amoffat" , "--system" , "--shell" , "/bin/bash" , "--no-create-home" ) |
Finding Commands
"Which" finds the full path of a program, or returns None if it doesn't exist. This command is one of the few commands implemented as a Python function, and therefore doesn't rely on the "which" program actually existing.
1
2
3
4
| print sh.which( "python" ) # "/usr/bin/python" print sh.which( "ls" ) # "/bin/ls" if not sh.which( "supervisorctl" ): sh.apt_get( "install" , "supervisor" , "-y" ) |
There are many more features that you can use with sh, and you can find them all in the official documentation.
Baking
sh is capable of "baking" arguments into commands.
1
2
3
4
5
6
7
8
| # The idea here is that now every call to ls will have the “-la” arguments already specified. from sh import ls ls = ls.bake( "-la" ) print (ls) # "/usr/bin/ls -la" # resolves to "ls -la /" print (ls( "/" )) |
Baked ssh command
Calling "bake" on a command creates a callable object that automatically passes along all of the arguments passed into "bake".
1
2
3
4
5
6
7
| # Without baking, calling uptime on a server would be a lot to type out: serverX = ssh( "myserver.com" , "-p 1393" , "whoami" ) # To bake the common parameters into the ssh command myserver = sh.ssh.bake( "myserver.com" , p = 1393 ) print (myserver) # "/usr/bin/ssh myserver.com -p 1393" |
Now that the “myserver” callable represents a baked ssh command, you can call anything on the server easily:
1
2
3
4
5
6
| # resolves to "/usr/bin/ssh myserver.com -p 1393 tail /var/log/dumb_daemon.log -n 100" print (myserver.tail( "/var/log/dumb_daemon.log" , n = 100 )) # check the uptime print myserver.uptime() 15 : 09 : 03 up 61 days, 22 : 56 , 0 users, load average: 0.12 , 0.13 , 0.05 |
For more advanced features, please see the official documentation.