使用Python中的sudo权限写入文件

use*_*889 12 python ioerror file-writing

如果非root用户为root拥有的文件运行以下代码,则会抛出错误,即使非root用户具有sudo权限:

try:
  f = open(filename, "w+")
except IOError:
  sys.stderr.write('Error: Failed to open file %s' % (filename))
f.write(response + "\n" + new_line)
f.close()
Run Code Online (Sandbox Code Playgroud)

有没有办法open(filename, "w+")使用sudo权限运行,或者执行此操作的替代功能?

L3v*_*han 11

你有几个选择:

  • 以root身份或使用sudo运行脚本
  • 设置setuid位并让root拥有该脚本(尽管在许多系统上这不适用于脚本,然后脚本可以被任何人调用)
  • 检测到您没有以root用户身份运行(os.geteuid() != 0),然后使用sudoinfront 调用自己(这将要求用户输入他们的密码)并退出:

import os
import sys
import subprocess

if os.geteuid() == 0:
    print("We're root!")
else:
    print("We're not root.")
    subprocess.call(['sudo', 'python3', *sys.argv])
    sys.exit()
Run Code Online (Sandbox Code Playgroud)

调用它看起来像这样:

$ python3 be_root.py
We're not root.
Password:
We're root!
Run Code Online (Sandbox Code Playgroud)

  • 我的错; 我目前正在研究 Python 2.7 项目,但我的虚拟环境搞混了。以为我在使用 Python 3。我将修改我的答案作为 Python 2.7 兼容的解决方案。 (2认同)

Pet*_*nry 7

TL; DR:

L3viathan的回复 有一个SynaxError编辑:在Python 3.5+上工作正常。这是Python 3.4.3(默认在Ubuntu 16.04上分发)和以下版本:

if os.geteuid() == 0:
    # do root things
else:
    subprocess.call(['sudo', 'python3'] + sys.argv)  # modified
Run Code Online (Sandbox Code Playgroud)

但是,我采用了另一种方法,因为在我的用例中,它使周围的代码更简单:

if os.geteuid() != 0:
    os.execvp('sudo', ['sudo', 'python3'] + sys.argv)
# do root things
Run Code Online (Sandbox Code Playgroud)

讲解

L3viathan的答复取决于PEP 448,它已包含在Python 3.5中,并引入了允许星号引数扩展的其他上下文。对于Python 3.4及以下版本,列表串联可用于执行相同的操作:

import os
import sys
import subprocess

if os.geteuid() == 0:
    print("We're root!")
else:
    print("We're not root.")
    subprocess.call(['sudo', 'python3'] + sys.argv)  # modified
Run Code Online (Sandbox Code Playgroud)

但是请注意: subprocess.call()启动一个子进程,这意味着在root完成运行脚本之后,原始用户也将继续运行其脚本。这意味着您需要将提升的逻辑放在if/else块的一侧,这样当原始脚本完成时,它就不会尝试运行任何需要提升的逻辑(L3viathan的示例执行此操作)。

这并不一定是一件坏事-这意味着普通/高架逻辑都可以用同一脚本编写并很好地分开-但是我的任务需要所有逻辑的根。如果其他块将为空,我不想浪费缩进级别,并且我还没有意识到使用子进程会如何影响事物,所以我尝试了以下方法:

import os
import sys
import subprocess

if os.geteuid() != 0:
    subprocess.call(['sudo', 'python3'] + sys.argv)

# do things that require root
Run Code Online (Sandbox Code Playgroud)

...它当然失败了,因为完成root任务后,普通用户恢复了其脚本的执行并尝试运行需要root的语句。

然后,我发现了这个Gist建议os.execvp()-它代替了正在运行的进程,而不是启动子进程:

import os
import sys

if os.geteuid() != 0:
    os.execvp('sudo', ['sudo', 'python3'] + sys.argv)  # final version

# do things that require root
Run Code Online (Sandbox Code Playgroud)

这看起来像预期的那样,节省了缩进级别和3行代码。

警告:大os.execvp()约十分钟前我还不知道,关于它的使用可能存在的陷阱或微妙之处,我一无所知。YMMV。


JS.*_*JS. 5

另一种选择是将文件写入您有权访问的目录,然后将sudo mv文件写入您无权访问的目录。


Mat*_*lan 2

您的脚本仅限于其运行时所使用的权限,因为您在没有 root 权限的情况下无法更改用户。

正如 Rob 所说,在不更改文件权限的情况下执行此操作的唯一方法是使用sudo.

sudo python ./your_file.py
Run Code Online (Sandbox Code Playgroud)