如何在 Windows 上以提升的权限运行脚本
- 2024-12-13 08:37:00
- admin 原创
- 89
问题描述:
我正在编写一个需要执行管理任务的 pyqt 应用程序。我更愿意以提升权限的方式启动我的脚本。我知道这个问题在 SO 或其他论坛中被问过很多次。但人们建议的解决方案是查看这个 SO 问题
从 Python 脚本中请求 UAC 提升?
但是,我无法执行链接中给出的示例代码。我已将此代码放在主文件顶部并尝试执行它。
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."
它实际上请求提升权限,但打印行从未执行。有人可以帮助我成功运行上述代码。
解决方案 1:
截至 2023 年 2 月 19 日的更新
以下脚本的更新现在已作为同一作者的 Python 包发布。您可以从 PyPi 安装它,其网址为https://pypi.org/project/pyuac/,源代码/主页位于https://github.com/Preston-Landers/pyuac。使用以下命令安装它:
pip install pyuac
pip install pypiwin32
该包的直接用法是:
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.
或者,如果您希望使用装饰器:
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()
原始答案
感谢大家的回复。我的脚本使用了 Preston Landers 在 2010 年编写的模块/脚本。在浏览了两天的互联网后,我找到了这个脚本。它深深地隐藏在 pywin32 邮件列表中。使用此脚本,可以更轻松地检查用户是否是管理员,如果不是,则请求 UAC/管理员权限。它在单独的窗口中提供输出以显示代码正在做什么。脚本中还包含如何使用代码的示例。为了让所有在 Windows 上寻找 UAC 的人受益,请查看此代码。它可以从您的主脚本中以类似的方式使用:-
import admin
if not admin.isUserAdmin():
admin.runAsAdmin()
实际代码(在模块中)是:-
#!/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())
解决方案 2:
在对您从某人那里获取的代码的答案的评论中,他说ShellExecuteEx 不会将其 STDOUT 发布回原始 shell。因此,即使代码可能运行良好,您也不会看到“我现在是 root ”。
不要打印某些内容,而是尝试写入文件:
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"
然后查看文件。
解决方案 3:
我找到了解决这个问题的一个非常简单的方法。
创建快捷方式
python.exe
将快捷方式目标更改为类似
C:xxx...python.exe your_script.py
单击快捷方式属性面板中的“高级...”,然后单击选项“以管理员身份运行”
我不确定这些选项的拼写是否正确,因为我使用的是中文版的Windows。
解决方案 4:
这是一个只需要 ctypes 模块的解决方案。支持 pyinstaller 包装程序。
#!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, )
解决方案 5:
以下是使用 stdout 重定向的解决方案:
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, \n 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()
解决方案 6:
制作批处理文件
添加 python.exe“(你的 py 文件在这里)”并加上引号
保存批处理文件
右键单击,然后单击以管理员身份运行
解决方案 7:
值得一提的是,如果您打算使用PyInstaller打包您的应用程序并且希望避免自己支持该功能,您可以传递--uac-admin
或--uac-uiaccess
参数以便在启动时请求 UAC 提升。
解决方案 8:
JetBrains 的WinElevator(已签名的 elevator.exe 和 launcher.exe 可在此处获得)允许您生成一个请求提升权限的子进程,同时保持 stdin/stdout/stderr 完好无损:
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.
解决方案 9:
此外,如果您的工作目录与您可以使用 lpDirectory 不同
procInfo = ShellExecuteEx(nShow=showCmd,
lpVerb=lpVerb,
lpFile=cmd,
lpDirectory= unicode(direc),
lpParameters=params)
如果更改路径不是一个理想的选择,则删除 python 3.X 的 unicode 会很方便
解决方案 10:
这对我有用:
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}
", 0)
if __name__ = '__main__':
run_as(required_command, required_password)
以下是我对上述代码使用的参考:
https://win32com.goermezer.de/microsoft/windows/controlling-applications-via-sendkeys.html
https://www.oreilly.com/library/view/python-cookbook/0596001673/ch07s16.html
解决方案 11:
使用 pyuac 它是对原始管理脚本的更新,由 Preston Landers 编写 python 项目链接:https://pypi.org/project/pyuac/
Github:https://github.com/Preston-Landers/pyuac
这对我有用
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()
解决方案 12:
我可以确认 delphifirst 的解决方案有效,并且是解决以提升的权限运行 python 脚本问题的最简单、最便捷的解决方案。
我创建了一个指向 python 可执行文件 (python.exe) 的快捷方式,然后通过在调用 python.exe 后添加我的脚本名称来修改快捷方式。接下来,我在快捷方式的“兼容性选项卡”上选中了“以管理员身份运行”。执行快捷方式时,您会收到一个提示,要求您允许以管理员身份运行脚本。
我的特定 Python 应用程序是一个安装程序。该程序允许安装和卸载另一个 Python 应用程序。就我而言,我创建了两个快捷方式,一个名为“appname install”,另一个名为“appname uninstall”。这两个快捷方式之间的唯一区别是 Python 脚本名称后面的参数。在安装程序版本中,参数为“install”。在卸载版本中,参数为“uninstall”。安装程序脚本中的代码评估提供的参数并根据需要调用适当的函数(install 或 uninstall)。
我希望我的解释能够帮助其他人更快地弄清楚如何以提升的权限运行 Python 脚本。
解决方案 13:
确保路径中有 python,如果没有,请按 win 键 + r,输入“%appdata%”(不带引号)打开本地目录,然后转到程序目录,打开 python,然后选择您的 python 版本目录。单击文件选项卡并选择复制路径并关闭文件资源管理器。
然后再次按 win 键 + r,输入 control 并按回车键。搜索环境变量。单击结果,您将看到一个窗口。在右下角单击环境变量。在系统端找到路径,选择它并单击编辑。在新窗口中,单击新建并将路径粘贴到其中。单击确定,然后在第一个窗口中应用。重启电脑。然后最后一次按 win + r,输入 cmd 并按 ctrl + shift + enter。授予权限并打开文件资源管理器,转到您的脚本并复制其路径。返回 cmd,输入“python”并粘贴路径,然后按回车键。完成
解决方案 14:
我想要一个更强大的版本,所以我最终得到了一个模块,它允许:如果需要,UAC 请求,从非特权实例打印和记录(使用 ipc 和网络端口)和一些其他功能。用法只是在脚本中插入 elevateme():在非特权中,它会监听特权打印/记录,然后退出并返回 false,在特权实例中它会立即返回 true。支持 pyinstaller。
原型:
# 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):
管理员工具
#!/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 "
" wherever it is, however usually is or string or
if "
" not in mystdout.getvalue():
if mystdout.getvalue() != "
":
#__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("
")
#__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+"
", 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())
解决方案 15:
import ctypes
import sys
import time
def main():
print("program running with admin rights")
# Writing to a file in the Windows folder
with open(r"C:Windowspython_admin_test.txt", "w") as f:
f.write("admin access granted")
time.sleep(3)
if __name__ == "__main__":
if not ctypes.windll.shell32.IsUserAnAdmin():
print("requesting for admin rights")
args = f'"{sys.argv[0]}" {" ".join(sys.argv[1:])}'
ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, args, None, 1)
else:
main()
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理必备:盘点2024年13款好用的项目管理软件
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)