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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/39499233/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-19 22:20:21  来源:igfitidea点击:

Write to a file with sudo privileges in Python

pythonfile-writingioerror

提问by user2824889

The following code throws an error if it's run by a non-root user for a file owned by root, even when the non-root user has sudo privileges:

如果由非 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()

Is there a way to run open(filename, "w+")with sudo privileges, or an alternative function that does this?

有没有办法以open(filename, "w+")sudo 权限运行,或者有一个替代功能可以做到这一点?

回答by L3viathan

You have a few options:

您有几个选择:

  • Run your script as root or with sudo
  • Set the setuid bit and have root own the script (although on many systems this won't work with scripts, and then the script will be callable by anyone)
  • Detect that you're not running as root (os.geteuid() != 0), then call yourself with sudoinfront (which will ask the user to enter their password) and exit:
  • 以 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()

Calling it looks like this:

调用它看起来像这样:

$ python3 be_root.py
We're not root.
Password:
We're root!

回答by Peter Henry

TL;DR:

特尔;博士:

L3viathan's replyhas a SynaxErrorEdit: works fine on Python 3.5+. Here is a version for Python 3.4.3 (distributed by default on Ubuntu 16.04) and below:

L3viathan 的回复有一个 SynaxErrorEdit: 在 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

However, I went with a different approach because it made the code around it simpler in my use case:

但是,我采用了不同的方法,因为它使我的用例中的代码更简单:

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

Explaination

说明

L3viathan's replydepends on PEP 448, which was included in Python 3.5 and introduced additional contexts where star argument expansion is permitted. For Python 3.4 and below, list concatenation can be used to do the same thing:

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

But note:subprocess.call()launches a child process, meaning after root finishes running the script, the original user will continue running their script as well. This means you need to put the elevated logic into one side of an if/elseblock so when the original script finishes, it doesn't try to run any of the logic that requires elevation (L3viathan's example does this).

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

This isn't necessarily a bad thing - it means both normal/elevated logic can be written in the same script and separated nicely - but my task required root for all the logic. I didn't want to waste an indentation level if the other block was going to be empty, and I hadn't realized how using a child process would affect things, so I tried this:

这不一定是件坏事——这意味着正常/高级逻辑都可以用同一个脚本编写并很好地分开——但是我的任务需要所有逻辑的根。如果另一个块是空的,我不想浪费缩进级别,而且我还没有意识到使用子进程会如何影响事情,所以我尝试了这个:

import os
import sys
import subprocess

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

# do things that require root

...and it broke of course, because after root was done, the regular user resumed execution of its script and tried to run the statements that required root.

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

Then I found this Gistrecommending os.execvp()- which replaces the running process instead of launching a child:

然后我发现这个 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

This appears to behave as expected, saves an indentation level and 3 lines of code.

这似乎按预期运行,节省了缩进级别和 3 行代码。

Caveat: I didn't know about os.execvp()ten minutes ago, and don't know anything yet about possible pitfalls or subtleties around its usage. YMMV.

警告:os.execvp()十分钟前我还不知道,也不知道它的使用可能存在的陷阱或微妙之处。天啊。

回答by Adam

Having possibility to using sudodon't give you any privileges if you don't actually use it. So as other guys suggested you probably should just start your program with use of sudo. But if you don't like this idea (I don't see any reason for that) you can do other trick.

sudo如果您实际上不使用它,则有可能使用它不会给您任何特权。因此,正如其他人建议的那样,您可能应该使用sudo. 但是如果你不喜欢这个想法(我看不出有任何理由)你可以做其他的把戏。

Your script can check if is run with root privileges or if it work only with user privileges. Than script can actually run itself with higher privileges. Here you have an example (please note that storing password in source code isn't a good idea).

您的脚本可以检查是使用 root 权限运行还是仅使用用户权限运行。比脚本实际上可以以更高的权限运行自己。这里有一个示例(请注意,在源代码中存储密码不是一个好主意)。

import os.path
import subprocess

password_for_sudo = 'pass'

def started_as_root():
    if subprocess.check_output('whoami').strip() == 'root':
        return True
    return False

def runing_with_root_privileges():
    print 'I have the power!'

def main():
    if started_as_root():
        print 'OK, I have root privileges. Calling the function...'
        runing_with_root_privileges()
    else:
        print "I'm just a user. Need to start new process with root privileges..."
        current_script = os.path.realpath(__file__)
        os.system('echo %s|sudo -S python %s' % (password_for_sudo, current_script))

if __name__ == '__main__':
    main()

Output:

输出:

$ python test.py
I'm just a user. Need to start new process with root privileges...
OK, I have root privileges. Calling the function...
I have the power!

$ sudo python test.py
OK, I have root privileges.
Calling the function...
I have the power!

回答by Matt Olan

Your script is limited to the permissions it is run with as you cannot change users without already having root privileges.

您的脚本仅限于它运行的权限,因为您不能在没有 root 权限的情况下更改用户。

As Rob said, the only way to do this without changing your file permissions is to run with sudo.

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

sudo python ./your_file.py

回答by portusato

I like L3viathan's reply. I'd add a fourth option: Use subprocess.Popen() to execute an sh command to write the file. Something like subprocess.Popen('sudo echo \'{}\' > {}'.format(new_line, filename)).

我喜欢 L3viathan 的回复。我要添加第四个选项:使用 subprocess.Popen() 执行 sh 命令来写入文件。类似的东西subprocess.Popen('sudo echo \'{}\' > {}'.format(new_line, filename))