简体   繁体   English

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

[英]Write to a file with sudo privileges in Python

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?有没有办法以 sudo 权限运行open(filename, "w+") ,或者执行此操作的替代函数?

You have a few options:您有几个选择:

  • Run your script as root or with sudo以 root 身份或使用 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)设置 setuid 位并让 root 拥有脚本(尽管在许多系统上这不适用于脚本,然后任何人都可以调用该脚本)
  • Detect that you're not running as root ( os.geteuid() != 0 ), then call yourself with sudo infront (which will ask the user to enter their password) and exit:检测您没有以 root os.geteuid() != 0运行( os.geteuid() != 0 ),然后使用sudo infront 调用自己(这将要求用户输入密码)并退出:

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!

TL;DR:特尔;博士:

L3viathan's reply has a SynaxError Edit: works fine on Python 3.5+. L3viathan 的回复有一个 SynaxError Edit: 在 Python 3.5+ 上工作正常。 Here is a version for Python 3.4.3 (distributed by default on Ubuntu 16.04) and below:这是 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 reply depends on PEP 448 , which was included in Python 3.5 and introduced additional contexts where star argument expansion is permitted. L3viathan 的回复取决于PEP 448 ,它包含在 Python 3.5 中并引入了允许星形参数扩展的附加上下文。 For Python 3.4 and below, list concatenation can be used to do the same thing:对于 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.请注意: subprocess.call()启动一个子进程,这意味着在 root 完成运行脚本后,原始用户也将继续运行他们的脚本。 This means you need to put the elevated logic into one side of an if/else block so when the original script finishes, it doesn't try to run any of the logic that requires elevation (L3viathan's example does this).这意味着您需要将提升的逻辑放在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 Gist recommending 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.警告:十分钟前我不知道os.execvp() ,并且对它的使用可能存在的陷阱或微妙之处一无所知。 YMMV.天啊。

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

Having possibility to using sudo don't give you any privileges if you don't actually use it.如果您实际上不使用它,则可以使用sudo不会给您任何特权。 So as other guys suggested you probably should just start your program with use of sudo .因此,正如其他人所建议的那样,您可能应该使用sudo启动您的程序。 But if you don't like this idea (I don't see any reason for that) you can do other trick.但是如果你不喜欢这个想法(我看不出有任何理由)你可以做其他的把戏。

Your script can check if is run with root privileges or if it work only with user privileges.您的脚本可以检查是使用 root 权限运行还是仅使用用户权限运行。 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).这里有一个示例(请注意,在源代码中存储密码不是一个好主意)。

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!

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

I like L3viathan's reply.我喜欢 L3viathan 的回复。 I'd add a fourth option: Use subprocess.Popen() to execute an sh command to write the file.我要添加第四个选项:使用 subprocess.Popen() 执行 sh 命令来写入文件。 Something like subprocess.Popen('sudo echo \\'{}\\' > {}'.format(new_line, filename)) .类似于subprocess.Popen('sudo echo \\'{}\\' > {}'.format(new_line, filename))

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM