I want to store into a variable the last output of a subprocess after the user performs a Keyboard Interrupt. My problem is mainly with a subprocess without end, i.e. tail in my exemple below. Here is my code:
class Testclass:
def Testdef(self):
try:
global out
print "Tail running"
tail_cmd='tail -f log.Reconnaissance'
proc = subprocess.Popen([tail_cmd], stdout=subprocess.PIPE, shell=True)
(out, err) = proc.communicate()
except KeyboardInterrupt:
print("KeyboardInterrupt received, stopping…")
finally:
print "program output:", out
if __name__ == "__main__":
app = Testclass()
app.Testdef()
Below is its output, which I don't understand at this moment.
Tail running
program output:
Traceback (most recent call last):
File "./2Test.py", line 19, in <module>
app.Testdef()
File "./2Test.py", line 15, in Testdef
print "program output:", out
NameError: global name 'out' is not defined
out not being defined indicates that the process proc.communicate() did not return any values, otherwise it would have populated your tuple (out, err). Now to find out whether the communicate() method was supposed to return or whether, more likely, your keyboard interrupt simply killed it, thus preventing out from being defined.
I assume you imported the subprocess module, but make sure you do that first. I rewrote your program without using global out or the try statements.
import subprocess
class Testclass:
def __init__(self,out): # allows you to pass in the value of out
self.out = out # makes out a member of this class
def Testdef(self):
print("Tail running")
tail_cmd='tail -f log.Reconnaissance'
proc = subprocess.Popen([tail_cmd], stdout=subprocess.PIPE, shell=True)
# Perhaps this is where you want to implement the try:
(self.out, err) = proc.communicate()
# and here the except:
# and here the finally:
if __name__ == "__main__":
app = Testclass(1) # pass 1 (or anything for testing) to the out variable
app.Testdef()
print('%r' % app.out) # print the contents of the out variable
# i get an empty string, ''
So as-is this program runs once. There is nothing in out. I believe to create a meaningful example of the user doing a keyboard interrupt, we need the program to be doing something that can be interrupted. Maybe I can provide an example in the future...
i'm trying to make my python program interactive in command line, user should be able to do stuff like :
python myprogram.py --create
then
python myprogram.py --send
The problem in this when is that the program stop and restart each time so i lose my variable and object that i created with the first command.
I'm using argparse on this way:
parser = argparse.ArgumentParser()
parser.add_argument('-c','--create' ,help='',action='store_true')
parser.add_argument('-s','--send',help='',action='store_true')
args = parser.parse_args()
if args.create:
create()
elif args.send :
send()
I don't want to stop the program between the command, how to do this ?
example : https://coderwall.com/p/w78iva
Here's a simple interactive script. I use argparse to parse the input lines, but otherwise it is not essential to the action. Still it can be an handy way of adding options to your 'create' command. For example, ipython uses argparse to handle its %magic commands:
import argparse
parser = argparse.ArgumentParser(prog='PROG', description='description')
parser.add_argument('cmd', choices=['create','delete','help','quit'])
while True:
astr = raw_input('$: ')
# print astr
try:
args = parser.parse_args(astr.split())
except SystemExit:
# trap argparse error message
print 'error'
continue
if args.cmd in ['create', 'delete']:
print 'doing', args.cmd
elif args.cmd == 'help':
parser.print_help()
else:
print 'done'
break
This could be stripped down to the while loop, the raw_input line, and your own evaluation of the astr variable.
The keys to using argparse here are:
parse_args can take a list of strings (the result of split()) instead of using the default sys.argv[1:].
if parse_args sees a problem (or '-h') it prints a message and tries to 'exit'. If you want to continue, you need to trap that error, hence the try block.
the output of parse_args is a simple namespace object. You access the arguments as attributes.
you could easily substitute your own parser.
The diffrence in cmd and argparse is that cmd is a "line-oriented command interpreter" while argparse is a parser for sys.argv.
Your example parses sys.argv that you pass while running your program and then if it gets the value you start a function and then quits.
argparse will only parse the sys.argv while running the program.
You could add some code to be able to work with the args you pass like a function or class or make in program menu that you could operate with raw_input.
Example:
class Main():
def __init__(self, create=None, send=None):
if create:
self.create(create)
elif send:
self.send(send)
option = raw_input('What do you want to do now?')
print option
def create(self, val):
print val
def send(self, val):
print val
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument('-c','--create' ,help='',action='store_true')
parser.add_argument('-s','--send',help='',action='store_true')
args = parser.parse_args()
Main(args.create, args.send)
Other then that Python argparse and controlling/overriding the exit status code or python argparse - add action to subparser with no arguments? might help.
In the first it shows how you can override the quit and in the second how can you add subcommands or quitactions.
I am writing a pyqt application which require to execute admin task. I would prefer to start my script with elevate privilege. I am aware that this question is asked many times in SO or in other forum. But the solution people are suggesting is to have a look at this SO question
Request UAC elevation from within a Python script?
However, I am unable to execute the sample code given in the link. I have put this code on top of the main file and tried to execute it.
import os
import sys
import win32com.shell.shell as shell
ASADMIN = 'asadmin'
if sys.argv[-1] != ASADMIN:
script = os.path.abspath(sys.argv[0])
params = ' '.join([script] + sys.argv[1:] + [ASADMIN])
shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params)
sys.exit(0)
print "I am root now."
It actually ask permission to elevate but print line never get executed. Somebody can help me to run the above code successfully.
Update as on 19-02-2023
The update to the below script is now alive as a Python package by the same author. You can install it from PyPi which lives at https://pypi.org/project/pyuac/ and the source code/ home page is located at https://github.com/Preston-Landers/pyuac. Install it using:
pip install pyuac
Direct usage of the package is:
import pyuac
def main():
print("Do stuff here that requires being run as an admin.")
# The window will disappear as soon as the program exits!
input("Press enter to close the window. >")
if __name__ == "__main__":
if not pyuac.isUserAdmin():
print("Re-launching as admin!")
pyuac.runAsAdmin()
else:
main() # Already an admin here.
or if you wish to use it using decorater:
from pyuac import main_requires_admin
#main_requires_admin
def main():
print("Do stuff here that requires being run as an admin.")
# The window will disappear as soon as the program exits!
input("Press enter to close the window. >")
if __name__ == "__main__":
main()
Original answer
Thank you all for your reply. I got my script working with the module/ script written by Preston Landers in 2010. After two days of browsing the internet, I could find the script as it was deeply hidden in the pywin32 mailing list. With this script, it is easier to check if the user is admin, and if not then ask for UAC/ admin right. It does provide output in separate windows to find out what the code is doing. An example of how to use the code is also included in the script. For the benefit of all who are looking for UAC on windows have a look at this code. I hope it helps someone looking for the same solution. It can be used something like this from your main script:-
import admin
if not admin.isUserAdmin():
admin.runAsAdmin()
The actual code is:-
#!/usr/bin/env python
# -*- coding: utf-8; mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vim: fileencoding=utf-8 tabstop=4 expandtab shiftwidth=4
# (C) COPYRIGHT © Preston Landers 2010
# Released under the same license as Python 2.6.5
import sys, os, traceback, types
def isUserAdmin():
if os.name == 'nt':
import ctypes
# WARNING: requires Windows XP SP2 or higher!
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
traceback.print_exc()
print "Admin check failed, assuming not an admin."
return False
elif os.name == 'posix':
# Check for root on Posix
return os.getuid() == 0
else:
raise RuntimeError, "Unsupported operating system for this module: %s" % (os.name,)
def runAsAdmin(cmdLine=None, wait=True):
if os.name != 'nt':
raise RuntimeError, "This function is only implemented on Windows."
import win32api, win32con, win32event, win32process
from win32com.shell.shell import ShellExecuteEx
from win32com.shell import shellcon
python_exe = sys.executable
if cmdLine is None:
cmdLine = [python_exe] + sys.argv
elif type(cmdLine) not in (types.TupleType,types.ListType):
raise ValueError, "cmdLine is not a sequence."
cmd = '"%s"' % (cmdLine[0],)
# XXX TODO: isn't there a function or something we can call to massage command line params?
params = " ".join(['"%s"' % (x,) for x in cmdLine[1:]])
cmdDir = ''
showCmd = win32con.SW_SHOWNORMAL
#showCmd = win32con.SW_HIDE
lpVerb = 'runas' # causes UAC elevation prompt.
# print "Running", cmd, params
# ShellExecute() doesn't seem to allow us to fetch the PID or handle
# of the process, so we can't get anything useful from it. Therefore
# the more complex ShellExecuteEx() must be used.
# procHandle = win32api.ShellExecute(0, lpVerb, cmd, params, cmdDir, showCmd)
procInfo = ShellExecuteEx(nShow=showCmd,
fMask=shellcon.SEE_MASK_NOCLOSEPROCESS,
lpVerb=lpVerb,
lpFile=cmd,
lpParameters=params)
if wait:
procHandle = procInfo['hProcess']
obj = win32event.WaitForSingleObject(procHandle, win32event.INFINITE)
rc = win32process.GetExitCodeProcess(procHandle)
#print "Process handle %s returned code %s" % (procHandle, rc)
else:
rc = None
return rc
def test():
rc = 0
if not isUserAdmin():
print "You're not an admin.", os.getpid(), "params: ", sys.argv
#rc = runAsAdmin(["c:\\Windows\\notepad.exe"])
rc = runAsAdmin()
else:
print "You are an admin!", os.getpid(), "params: ", sys.argv
rc = 0
x = raw_input('Press Enter to exit.')
return rc
if __name__ == "__main__":
sys.exit(test())
in comments to the answer you took the code from someone says ShellExecuteEx doesn't post its STDOUT back to the originating shell. so you will not see "I am root now", even though the code is probably working fine.
instead of printing something, try writing to a file:
import os
import sys
import win32com.shell.shell as shell
ASADMIN = 'asadmin'
if sys.argv[-1] != ASADMIN:
script = os.path.abspath(sys.argv[0])
params = ' '.join([script] + sys.argv[1:] + [ASADMIN])
shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params)
sys.exit(0)
with open("somefilename.txt", "w") as out:
print >> out, "i am root"
and then look in the file.
I found a very easy solution to this problem.
Create a shortcut for python.exe
Change the shortcut target into something like C:\xxx\...\python.exe your_script.py
Click "advance..." in the property panel of the shortcut, and click the option "run as administrator"
I'm not sure whether the spells of these options are right, since I'm using Chinese version of Windows.
Here is a solution which needed ctypes module only. Support pyinstaller wrapped program.
#!python
# coding: utf-8
import sys
import ctypes
def run_as_admin(argv=None, debug=False):
shell32 = ctypes.windll.shell32
if argv is None and shell32.IsUserAnAdmin():
return True
if argv is None:
argv = sys.argv
if hasattr(sys, '_MEIPASS'):
# Support pyinstaller wrapped program.
arguments = map(unicode, argv[1:])
else:
arguments = map(unicode, argv)
argument_line = u' '.join(arguments)
executable = unicode(sys.executable)
if debug:
print 'Command line: ', executable, argument_line
ret = shell32.ShellExecuteW(None, u"runas", executable, argument_line, None, 1)
if int(ret) <= 32:
return False
return None
if __name__ == '__main__':
ret = run_as_admin()
if ret is True:
print 'I have admin privilege.'
raw_input('Press ENTER to exit.')
elif ret is None:
print 'I am elevating to admin privilege.'
raw_input('Press ENTER to exit.')
else:
print 'Error(ret=%d): cannot elevate privilege.' % (ret, )
Here is a solution with an stdout redirection:
def elevate():
import ctypes, win32com.shell.shell, win32event, win32process
outpath = r'%s\%s.out' % (os.environ["TEMP"], os.path.basename(__file__))
if ctypes.windll.shell32.IsUserAnAdmin():
if os.path.isfile(outpath):
sys.stderr = sys.stdout = open(outpath, 'w', 0)
return
with open(outpath, 'w+', 0) as outfile:
hProc = win32com.shell.shell.ShellExecuteEx(lpFile=sys.executable, \
lpVerb='runas', lpParameters=' '.join(sys.argv), fMask=64, nShow=0)['hProcess']
while True:
hr = win32event.WaitForSingleObject(hProc, 40)
while True:
line = outfile.readline()
if not line: break
sys.stdout.write(line)
if hr != 0x102: break
os.remove(outpath)
sys.stderr = ''
sys.exit(win32process.GetExitCodeProcess(hProc))
if __name__ == '__main__':
elevate()
main()
It worth mentioning that if you intend to package your application with PyInstaller and wish to avoid supporting that feature by yourself, you can pass the --uac-admin or --uac-uiaccess argument in order to request UAC elevation on start.
make a batch file
add python.exe "(your py file here)" with the quotation marks
save the batch file
right click, then click run as administrator
Also if your working directory is different than you can use lpDirectory
procInfo = ShellExecuteEx(nShow=showCmd,
lpVerb=lpVerb,
lpFile=cmd,
lpDirectory= unicode(direc),
lpParameters=params)
Will come handy if changing the path is not a desirable option
remove unicode for python 3.X
This worked for me:
import win32com.client as client
required_command = "cmd" # Enter your command here
required_password = "Simple1" # Enter your password here
def run_as(required_command, required_password):
shell = client.Dispatch("WScript.shell")
shell.Run(f"runas /user:administrator {required_command}")
time.sleep(1)
shell.SendKeys(f"{required_password}\r\n", 0)
if __name__ = '__main__':
run_as(required_command, required_password)
Below are the references I used for above code:
https://win32com.goermezer.de/microsoft/windows/controlling-applications-via-sendkeys.html
https://www.oreilly.com/library/view/python-cookbook/0596001673/ch07s16.html
Use pyuac
it is the update to the orignal admin Script by Preston Landers
Link to python Projects: https://pypi.org/project/pyuac/
Github: https://github.com/Preston-Landers/pyuac
This worked for me it
from pyuac import main_requires_admin
#main_requires_admin
def main():
print("Do stuff here that requires being run as an admin.")
# The window will disappear as soon as the program exits!
input("Press enter to close the window. >")
if __name__ == "__main__":
main()
JetBrains' WinElevator (signed elevator.exe and launcher.exe available here) allows you to spawn a subprocess that requests elevated privileges while keeping stdin/stdout/stderr intact:
import ctypes
import subprocess
import sys
if not ctypes.windll.shell32.IsUserAnAdmin():
print("not an admin, restarting...")
subprocess.run(["launcher.exe", sys.executable, *sys.argv])
else:
print("I'm an admin now.")
> python example.py
not an admin, restarting...
# UAC prompt is shown
I'm an admin now.
I can confirm that the solution by delphifirst works and is the easiest, simplest solution to the problem of running a python script with elevated privileges.
I created a shortcut to the python executable (python.exe) and then modified the shortcut by adding my script's name after the call to python.exe. Next I checked "run as administrator" on the "compatibility tab" of the shortcut. When the shortcut is executed, you get a prompt asking permission to run the script as an administrator.
My particular python application was an installer program. The program allows installing and uninstalling another python app. In my case I created two shortcuts, one named "appname install" and the other named "appname uninstall". The only difference between the two shortcuts is the argument following the python script name. In the installer version the argument is "install". In the uninstall version the argument is "uninstall". Code in the installer script evaluates the argument supplied and calls the appropriate function (install or uninstall) as needed.
I hope my explanation helps others more quickly figure out how to run a python script with elevated privileges.
Make sure you have python in path,if not,win key + r, type in "%appdata%"(without the qotes) open local directory, then go to Programs directory ,open python and then select your python version directory. Click on file tab and select copy path and close file explorer.
Then do win key + r again, type control and hit enter. search for environment variables. click on the result, you will get a window. In the bottom right corner click on environmental variables. In the system side find path, select it and click on edit.
In the new window, click on new and paste the path in there. Click ok and then apply in the first window. Restart your PC. Then do win + r for the last time, type cmd and do ctrl + shift + enter. Grant the previliges and open file explorer, goto your script and copy its path. Go back into cmd , type in "python" and paste the path and hit enter. Done
I wanted a more enhanced version so I ended up with a module which allows:
UAC request if needed, printing and logging from nonprivileged instance (uses ipc and a network port) and some other candies. usage is just insert elevateme() in your script: in nonprivileged it listen for privileged print/logs and then exits returning false, in privileged instance it returns true immediately.
Supports pyinstaller.
prototype:
# xlogger : a logger in the server/nonprivileged script
# tport : open port of communication, 0 for no comm [printf in nonprivileged window or silent]
# redir : redirect stdout and stderr from privileged instance
#errFile : redirect stderr to file from privileged instance
def elevateme(xlogger=None, tport=6000, redir=True, errFile=False):
winadmin.py
#!/usr/bin/env python
# -*- coding: utf-8; mode: python; py-indent-offset: 4; indent-tabs-mode: nil -*-
# vim: fileencoding=utf-8 tabstop=4 expandtab shiftwidth=4
# (C) COPYRIGHT © Preston Landers 2010
# (C) COPYRIGHT © Matteo Azzali 2020
# Released under the same license as Python 2.6.5/3.7
import sys, os
from traceback import print_exc
from multiprocessing.connection import Listener, Client
import win32event #win32com.shell.shell, win32process
import builtins as __builtin__ # python3
# debug suffixes for remote printing
dbz=["","","",""] #["J:","K:", "G:", "D:"]
LOGTAG="LOGME:"
wrconn = None
#fake logger for message sending
class fakelogger:
def __init__(self, xlogger=None):
self.lg = xlogger
def write(self, a):
global wrconn
if wrconn is not None:
wrconn.send(LOGTAG+a)
elif self.lg is not None:
self.lg.write(a)
else:
print(LOGTAG+a)
class Writer():
wzconn=None
counter = 0
def __init__(self, tport=6000,authkey=b'secret password'):
global wrconn
if wrconn is None:
address = ('localhost', tport)
try:
wrconn = Client(address, authkey=authkey)
except:
wrconn = None
wzconn = wrconn
self.wrconn = wrconn
self.__class__.counter+=1
def __del__(self):
self.__class__.counter-=1
if self.__class__.counter == 0 and wrconn is not None:
import time
time.sleep(0.1) # slows deletion but is enough to print stderr
wrconn.send('close')
wrconn.close()
def sendx(cls, mesg):
cls.wzconn.send(msg)
def sendw(self, mesg):
self.wrconn.send(msg)
#fake file to be passed as stdout and stderr
class connFile():
def __init__(self, thekind="out", tport=6000):
self.cnt = 0
self.old=""
self.vg=Writer(tport)
if thekind == "out":
self.kind=sys.__stdout__
else:
self.kind=sys.__stderr__
def write(self, *args, **kwargs):
global wrconn
global dbz
from io import StringIO # # Python2 use: from cStringIO import StringIO
mystdout = StringIO()
self.cnt+=1
__builtin__.print(*args, **kwargs, file=mystdout, end = '')
#handles "\n" wherever it is, however usually is or string or \n
if "\n" not in mystdout.getvalue():
if mystdout.getvalue() != "\n":
#__builtin__.print("A:",mystdout.getvalue(), file=self.kind, end='')
self.old += mystdout.getvalue()
else:
#__builtin__.print("B:",mystdout.getvalue(), file=self.kind, end='')
if wrconn is not None:
wrconn.send(dbz[1]+self.old)
else:
__builtin__.print(dbz[2]+self.old+ mystdout.getvalue(), file=self.kind, end='')
self.kind.flush()
self.old=""
else:
vv = mystdout.getvalue().split("\n")
#__builtin__.print("V:",vv, file=self.kind, end='')
for el in vv[:-1]:
if wrconn is not None:
wrconn.send(dbz[0]+self.old+el)
self.old = ""
else:
__builtin__.print(dbz[3]+self.old+ el+"\n", file=self.kind, end='')
self.kind.flush()
self.old=""
self.old=vv[-1]
def open(self):
pass
def close(self):
pass
def flush(self):
pass
def isUserAdmin():
if os.name == 'nt':
import ctypes
# WARNING: requires Windows XP SP2 or higher!
try:
return ctypes.windll.shell32.IsUserAnAdmin()
except:
traceback.print_exc()
print ("Admin check failed, assuming not an admin.")
return False
elif os.name == 'posix':
# Check for root on Posix
return os.getuid() == 0
else:
print("Unsupported operating system for this module: %s" % (os.name,))
exit()
#raise (RuntimeError, "Unsupported operating system for this module: %s" % (os.name,))
def runAsAdmin(cmdLine=None, wait=True, hidden=False):
if os.name != 'nt':
raise (RuntimeError, "This function is only implemented on Windows.")
import win32api, win32con, win32process
from win32com.shell.shell import ShellExecuteEx
python_exe = sys.executable
arb=""
if cmdLine is None:
cmdLine = [python_exe] + sys.argv
elif not isinstance(cmdLine, (tuple, list)):
if isinstance(cmdLine, (str)):
arb=cmdLine
cmdLine = [python_exe] + sys.argv
print("original user", arb)
else:
raise( ValueError, "cmdLine is not a sequence.")
cmd = '"%s"' % (cmdLine[0],)
params = " ".join(['"%s"' % (x,) for x in cmdLine[1:]])
if len(arb) > 0:
params += " "+arb
cmdDir = ''
if hidden:
showCmd = win32con.SW_HIDE
else:
showCmd = win32con.SW_SHOWNORMAL
lpVerb = 'runas' # causes UAC elevation prompt.
# print "Running", cmd, params
# ShellExecute() doesn't seem to allow us to fetch the PID or handle
# of the process, so we can't get anything useful from it. Therefore
# the more complex ShellExecuteEx() must be used.
# procHandle = win32api.ShellExecute(0, lpVerb, cmd, params, cmdDir, showCmd)
procInfo = ShellExecuteEx(nShow=showCmd,
fMask=64,
lpVerb=lpVerb,
lpFile=cmd,
lpParameters=params)
if wait:
procHandle = procInfo['hProcess']
obj = win32event.WaitForSingleObject(procHandle, win32event.INFINITE)
rc = win32process.GetExitCodeProcess(procHandle)
#print "Process handle %s returned code %s" % (procHandle, rc)
else:
rc = procInfo['hProcess']
return rc
# xlogger : a logger in the server/nonprivileged script
# tport : open port of communication, 0 for no comm [printf in nonprivileged window or silent]
# redir : redirect stdout and stderr from privileged instance
#errFile : redirect stderr to file from privileged instance
def elevateme(xlogger=None, tport=6000, redir=True, errFile=False):
global dbz
if not isUserAdmin():
print ("You're not an admin.", os.getpid(), "params: ", sys.argv)
import getpass
uname = getpass.getuser()
if (tport> 0):
address = ('localhost', tport) # family is deduced to be 'AF_INET'
listener = Listener(address, authkey=b'secret password')
rc = runAsAdmin(uname, wait=False, hidden=True)
if (tport> 0):
hr = win32event.WaitForSingleObject(rc, 40)
conn = listener.accept()
print ('connection accepted from', listener.last_accepted)
sys.stdout.flush()
while True:
msg = conn.recv()
# do something with msg
if msg == 'close':
conn.close()
break
else:
if msg.startswith(dbz[0]+LOGTAG):
if xlogger != None:
xlogger.write(msg[len(LOGTAG):])
else:
print("Missing a logger")
else:
print(msg)
sys.stdout.flush()
listener.close()
else: #no port connection, its silent
WaitForSingleObject(rc, INFINITE);
return False
else:
#redirect prints stdout on master, errors in error.txt
print("HIADM")
sys.stdout.flush()
if (tport > 0) and (redir):
vox= connFile(tport=tport)
sys.stdout=vox
if not errFile:
sys.stderr=vox
else:
vfrs=open("errFile.txt","w")
sys.stderr=vfrs
#print("HI ADMIN")
return True
def test():
rc = 0
if not isUserAdmin():
print ("You're not an admin.", os.getpid(), "params: ", sys.argv)
sys.stdout.flush()
#rc = runAsAdmin(["c:\\Windows\\notepad.exe"])
rc = runAsAdmin()
else:
print ("You are an admin!", os.getpid(), "params: ", sys.argv)
rc = 0
x = raw_input('Press Enter to exit.')
return rc
if __name__ == "__main__":
sys.exit(test())