Package: shell-proc
Installpipinstallshell_proc3.0.0 ChangesMajor changes! Too many differences between linux, windows cmd, and windows powershell.I separated everything into separate classes.Shell is the default for the OS.Can specify BashShell (LinuxShell), WindowsPowerShell, and WindowsCmdShell.Changed parallel function in an attempt to use background processes.Removed ParallelShell classAdded Command.raw_stdout and Command.raw_stderr.Added Shell.is_windows_cmd and Shell.is_powershell.Each shell has specific quote escape functions.This can be a giant pain and could be improved with regexAdded show_all_output and show_commands for how the output is displayed.Added extra and end arguments to the run function to add more control to the commands.2.0.0 ChangesThis library has recently been updated to support commands with stdin prompts.
I noticedgitandsshwould occasionally request pompt input from the user for authorization.
The old approach would get the exit code by sending an echo to stdin.
This would give the echo command as a user prompt.
To support this a lot of things had to change.
Windows users will notice some changes.I changed how the echo results works and am using“;”instead of a second command passed into stdin.I also changed the pipes to non-blocking allowing input prompts to be read.Before the pipe reading waited on a newline.Unfortunately, “;” did not really work with windows cmd.Windows operations were changed to use powershell.Powershell$?gives“True”or“False”, so it does not give a proper exit_code.Old cmd is still supported if you passuse_old_cmd=Trueinto the Shell. Not guaranteed to work.Addedinputwhich will only pass values into stdin without expecting a command to finish.RunRun a series of commands with results.fromshell_procimportShellwithShell()assh:sh('cd ..')ifsh.is_windows():cmd=sh('dir')else:cmd=sh('ls')# cmd (Command) Attributes: cmd, exit_code, stdout, stderrprint(cmd.stdout)Run a series of terminal commands.importsysfromshell_procimportShellwithShell(stdout=sys.stdout,stderr=sys.stderr)assh:sh.run('mkdir storage')sh('cd storage')# Same as sh.run()sh('echo Hello World! > hello.txt')ifsh.is_windows():sh('python -m venv ./winvenv')sh('./winvenv/Scripts/activate.bat')else:pwd=sh('pwd')sh('cd ~')sh('python3 -m venv ./lxvenv')sh('source ./lxvenv/bin/activate')sh('cd{}'.format(pwd.stdout))sh('pip install requests')sh('pip list')table='|{:_<20}|{:_<20}|{:_<20}|{:_<50}|'print(table.format('','','','').replace('|','_'))print(table.format("Exit Code","Has Error","Has Ouput","Command").replace('_',' '))print(table.format('','','',''))forcmdinsh.history:print(table.format(cmd.exit_code,cmd.has_error(),cmd.has_output(),cmd.cmd).replace('_',' '))print(table.format('','','','').replace('|','_'))Run without blocking every commandimportsysimporttimefromshell_procimportShellwithShell(stdout=sys.stdout,stderr=sys.stderr,blocking=False,wait_on_exit=True)assh:sh.run('mkdir storage')sh('cd storage')# Same as sh.run()sh('echo Hello World! > hello.txt')ifsh.is_windows():sh('python -m venv ./winvenv')sh('./winvenv/Scripts/activate.bat')else:pwd=sh('pwd')sh('cd ~')sh('python3 -m venv ./lxvenv')sh('source ./lxvenv/bin/activate')sh('cd{}'.format(pwd.stdout))sh('pip install requests')sh('pip list')print('---------- At exit (shows non-blocking until exit) ----------')time.sleep(1)print('1 Second has passed','Running:',sh.current_command)time.sleep(1)print('2 Seconds have passed','Running:',sh.current_command)time.sleep(1)print('3 Seconds have passed','Running:',sh.current_command)sh.wait()# Wait for all commands to finishManually call commands and check results.importioimportsysfromshell_procimportShell# Initialize and run taskssh=Shell('mkdir storage','cd storage','echo Hello World! > hello.txt',stderr=io.StringIO())# Manually run tasksifsh.is_windows():sh('python -m venv ./winvenv')sh('./winvenv/Scripts/activate.bat')else:pwd=sh('pwd')sh('cd ~')sh('python3 -m venv ./lxvenv')sh('source ./lxvenv/bin/activate')sh('cd{}'.format(pwd.stdout))# Not exactly success. If True no output was printed to stderr. Stderr could also be warning like need to update pipresults=sh.run('pip install requests')print("***** Successful install: ",results.exit_code==0)ifresults.exit_code!=0:sh.stderr.seek(0)# Move to start of io.StringIO()err=sh.stderr.read()# All text collected into stderr from subprocess stderrprint(err,file=sys.stderr)# sh.print_stderr()  # Also availablesh.stdout=io.StringIO()# Start saving output for new tasksresults=sh('pip list')print('***** Output Printed\n',results.stdout)sh('pip -V')print('pip -V =>',sh.last_command.stdout)print('All collected stdout')sh.stdout.seek(0)# Move to start of io.StringIO()print(sh.stdout.read(),end='',flush=True)# Print all read data# Should close when finished to stop threads from reading stdout and stderr subprocess.PIPE# (will close automatically eventually)sh.close()io.StringIO() HelpBelow are several functions to read data from stdout and io.StringIO()defread_io(fp):"""Return all of the human readable text from the io object."""try:iffp.seekable():fp.seek(0)out=fp.read()ifnotisinstance(out,str):out=out.decode('utf-8')returnoutexcept:return''defclear_io(fp):"""Try to clear the stdout"""text=read_io(fp)try:fp.truncate(0)except:passreturntextdefprint_io(fp,end='\n',file=None,flush=True):"""Print and clear the collected io."""iffileisNone:file=sys.stdoutprint(clear_io(fp),file=file,flush=True)Run PythonAdded support to call python in a subprocessfromshell_procimportShellwithShell(python_call='python3')assh:sh.python('-c','import os','print("My PID:", os.getpid())')Run ParallelAdded support to run parallel subprocessesimportsysimporttimefromshell_procimportShell,python_argswithShell(stdout=sys.stdout,stderr=sys.stderr,python_call='python3')assh:python_call="python3"ifsh.is_windows():python_call="../venv/Scripts/python"cmd=sh.parallel(*(python_args('-c','import os','import time',"print('My ID:',{id}, 'My PID:', os.getpid(), time.time())".format(id=i),python_call=python_call)foriinrange(10)))# Note: this will finish immediately and you should probably add an extra sleep like belowsh.wait()print('finished parallel')time.sleep(1)background={"end":"&","extra":"; sleep 2"}python_call="python3"ifShell.is_windows():python_call="../venv/Scripts/python"ifsh.is_powershell():background={"extra":"; Start-Sleep -Seconds 2"}else:background={"extra":"& waitfor /t 2 shellproc 2>Nul"}tasks=[]foriinrange(10):ifi==3:t=python_args('-c','import os','import time','time.sleep(1)',"print('My ID:',{id}, 'My PID:', os.getpid(), time.time())".format(id=i),python_call=python_call)else:t=python_args('-c','import os','import time',"print('My ID:',{id}, 'My PID:', os.getpid(), time.time())".format(id=i),python_call=python_call)tasks.append(t)cmd=sh.parallel(*tasks,**background)sh.wait()print('finished parallel')Use PipeThe pipe operator can be used with Command objects to take a completed command stdout and submit the text into a
new commands stdin.importsysfromshell_procimportShell,ShellExit,shell_argswithShell(stdout=sys.stdout,stderr=sys.stderr)assh:# One stepresults=sh('dir')|'find "run"'# Hard to tell where find output starts# Two Stepscmd=sh('dir')results=sh('find "run"',pipe_text=cmd.stdout)Input PromptsAs of version 2.0.0, Shell can work with input prompts.
I noticedgitandsshwould occasionally request pompt input from the user for authorization.
I wanted to support this use case.Input prompt code# prompt_me.pyprint("Greetings!")name=input("Hello, who am I talking to? ")print(f"It\'s nice to meet you{name!r}")Shell code# run shellimportsysfromshell_procimportShellwithShell(stdout=sys.stdout,stderr=sys.stderr)assh:print("Give user input when prompted")# Need block=False or will wait forever for input it cannot receivesh("python prompt_me.py",block=False)# Get actual input from uservalue=input()# Send input to stdin (without expecting this to run as a command)# This will finish the first command sh(python prompt_me.py)sh.input(value)sh.wait()# Manually wait for sh(python prompt_me.py) to finish# Test againsh("python prompt_me.py",block=False)sh.input("John Doe",wait=True)# Shell.__exit__ will wait for final exit_code from sh(python prompt_me.py)print("Exited successfully!")Output. Note, “Jane Doe” was entered in as input.Give user input when prompted
Greetings!
Hello, who am I talking to? Jane Doe
It's nice to meet you 'Jane Doe'
Greetings!
Hello, who am I talking to? It's nice to meet you 'John Doe'
Exited successfully!