我知道这篇文章有点老了,但我写了以下内容来解决我的问题(在 Linux 和 OS X 上以 root 身份运行 python 脚本)。
我编写了以下 bash 脚本来以管理员权限执行 bash/python 脚本(适用于 Linux 和 OS X 系统):
#!/bin/bash
if [ -z "$1" ]; then
echo "Specify executable"
exit 1
fi
EXE=$1
available(){
which $1 >/dev/null 2>&1
}
platform=`uname`
if [ "$platform" == "Darwin" ]; then
MESSAGE="Please run $1 as root with sudo or install osascript (should be installed by default)"
else
MESSAGE="Please run $1 as root with sudo or install gksu / kdesudo!"
fi
if [ `whoami` != "root" ]; then
if [ "$platform" == "Darwin" ]; then
# Apple
if available osascript
then
SUDO=`which osascript`
fi
else # assume Linux
# choose either gksudo or kdesudo
# if both are avilable check whoch desktop is running
if available gksudo
then
SUDO=`which gksudo`
fi
if available kdesudo
then
SUDO=`which kdesudo`
fi
if ( available gksudo && available kdesudo )
then
if [ $XDG_CURRENT_DESKTOP = "KDE" ]; then
SUDO=`which kdesudo`;
else
SUDO=`which gksudo`
fi
fi
# prefer polkit if available
if available pkexec
then
SUDO=`which pkexec`
fi
fi
if [ -z $SUDO ]; then
if available zenity; then
zenity --info --text "$MESSAGE"
exit 0
elif available notify-send; then
notify-send "$MESSAGE"
exit 0
elif available xmessage notify-send; then
xmessage -buttons Ok:0 "$MESSAGE"
exit 0
else
echo "$MESSAGE"
fi
fi
fi
if [ "$platform" == "Darwin" ]
then
$SUDO -e "do shell script \"$*\" with administrator privileges"
else
$SUDO $@
fi
基本上,我设置系统的方式是将子文件夹保存在 bin 目录中(例如 /usr/local/bin/pyscripts 在 /usr/local/bin 中),并创建指向可执行文件的符号链接。这对我来说有三个好处:
(1) 如果我有不同的版本,我可以通过更改符号链接轻松切换执行哪个版本,并保持 bin 目录更干净(例如 /usr/local/bin/gcc-versions/4.9/、/usr /local/bin/gcc-versions/4.8/, /usr/local/bin/gcc --> gcc-versions/4.8/gcc)
(2) 我可以存储带有扩展名的脚本(有助于在 IDE 中突出显示语法),但可执行文件不包含它们,因为我喜欢这种方式(例如 svn-tools --> pyscripts/svn-tools. py)
(3) 原因如下:
我将脚本命名为“run-as-root-wrapper”并将其放置在一个非常常见的路径中(例如 /usr/local/bin),因此 python 不需要任何特殊的东西来定位它。然后我有以下 run_command.py 模块:
import os
import sys
from distutils.spawn import find_executable
#===========================================================================#
def wrap_to_run_as_root(exe_install_path, true_command, expand_path = True):
run_as_root_path = find_executable("run-as-root-wrapper")
if(not run_as_root_path):
return False
else:
if(os.path.exists(exe_install_path)):
os.unlink(exe_install_path)
if(expand_path):
true_command = os.path.realpath(true_command)
true_command = os.path.abspath(true_command)
true_command = os.path.normpath(true_command)
f = open(exe_install_path, 'w')
f.write("#!/bin/bash\n\n")
f.write(run_as_root_path + " " + true_command + " $@\n\n")
f.close()
os.chmod(exe_install_path, 0755)
return True
在我的实际 python 脚本中,我有以下功能:
def install_cmd(args):
exe_install_path = os.path.join(args.prefix,
os.path.join("bin", args.name))
if(not run_command.wrap_to_run_as_root(exe_install_path, sys.argv[0])):
os.symlink(os.path.realpath(sys.argv[0]), exe_install_path)
因此,如果我有一个名为 TrackingBlocker.py 的脚本(我用来修改 /etc/hosts 文件以将已知跟踪域重新路由到 127.0.0.1 的实际脚本),当我调用“sudo /usr/local/bin /pyscripts/TrackingBlocker.py --prefix /usr/local --name ModifyTrackingBlocker install”(通过argparse模块处理的参数),它安装“/usr/local/bin/ModifyTrackingBlocker”,这是一个bash脚本执行
/usr/local/bin/run-as-root-wrapper /usr/local/bin/pyscripts/TrackingBlocker.py [args]
例如
ModifyTrackingBlocker add tracker.ads.com
执行:
/usr/local/bin/run-as-root-wrapper /usr/local/bin/pyscripts/TrackingBlocker.py add tracker.ads.com
然后显示获得添加权限所需的身份验证对话框:
127.0.0.1 tracker.ads.com
到我的主机文件(只能由超级用户写入)。
如果您想简化/修改它以仅以 root 身份运行某些命令,您可以简单地将其添加到您的脚本中(使用上面提到的必要导入 + 导入子进程):
def run_as_root(command, args, expand_path = True):
run_as_root_path = find_executable("run-as-root-wrapper")
if(not run_as_root_path):
return 1
else:
if(expand_path):
command = os.path.realpath(command)
command = os.path.abspath(command)
command = os.path.normpath(command)
cmd = []
cmd.append(run_as_root_path)
cmd.append(command)
cmd.extend(args)
return subprocess.call(' '.join(cmd), shell=True)
使用上述(在 run_command 模块中):
>>> ret = run_command.run_as_root("/usr/local/bin/pyscripts/TrackingBlocker.py", ["status", "display"])
>>> /etc/hosts is blocking approximately 16147 domains