有没有办法在Python中作为特定用户执行命令?

时间:2012-07-26 04:10:39

标签: python linux shell operating-system command

据我所知,有三种方法可以在Python中执行系统命令:

  1. os.system(command) -> exit_status

  2. os.popen(command [, mode='r' [, bufsize]]) -> pipe

  3. commands.getoutput(command)-> string

  4. 现在我需要控制系统命令的激励,除了:

    os.system('su xxx;' + command)

    还有其他更优雅的方式来达到同样的效果吗?

3 个答案:

答案 0 :(得分:2)

您提到的所有事情(顺便说一下,subprocess模块已成功完成)都是产生过程的方法。你听起来像是在寻找setuid。你可以调用一个能够做到这一点的函数(例如os.setuid),或者,根据你的脚本的不同,你可以像升级用户一样运行整个脚本。

答案 1 :(得分:1)

使用subprocess模块(实施@Julian's suggestion)以其他用户身份运行命令。它与@tMC's code类似,但处于更高级别:

import os
import pwd
from subprocess import check_output as qx

def change_user(uid, gid=None):
    if gid is None:
        gid = uid
    def preexec_fn():
        os.setgid(gid)
        os.setgroups([gid])
        os.setuid(uid)
    return preexec_fn

print(qx(['id']))
print(qx(['id'], preexec_fn=change_user(pwd.getpwnam('nobody').pw_uid), env={}))
print(qx(['id']))

在旧的Python版本上,您可能需要在子进程调用中添加close_fds=True以避免fd泄漏。

您可以使用cwd参数指定运行命令的目录,例如用户的主目录。

填充env字典以设置子流程的环境变量。

答案 2 :(得分:0)

虽然对于大多数实现而言可能处于“低级别”,但是对于某些人来说理解这在高级模块中是如何发生的可能是有教育意义的。

import os
import pwd

pout, pin = os.pipe()                                # Creates an anonymous pipe used to communicate with the child process

if not os.fork():                                    # The fork() syscall duplicated the process retuning 0 to the new child process.
    os.closerange(0, 2)                              # Closing stdin, stdout and stderr- they were inherited from the parent process
    os.dup2(pin, 1)                                  # Copy the input side of the IPC pipe to stdout (always fd 1)
    os.setuid(pwd.getpwnam('nobody').pw_uid)         # Change our user id to that of the user `nobody`.  Also see `setgid()`
    os.execl('/bin/whoami', 'whoami')                # Now that we've connected out stdout to the pipe connected to the parent process
                                                     #     and changed our user id to that of the user we want to run the command with
                                                     #     we can `exec()` the new process; replacing our currently running process, 
                                                     #     inheriting the env.
    os._exit(0)                                      # Since we called `exec` above, this is never eval'd, but is good form

os.wait()                                            # Back at the parent process- `wait()` will, well, wait until the child process exits.
os.close(pin)                                        # Close the input side of the pipe, the parent shouldn't write to. (bi-dirctional IPC
                                                     #     would require 2 pipes.  One in each direction
child_stdout_pipe = os.fdopen(pout, 'r')             # Open the output side of the IPC pipe
child_process_output = child_stdout_pipe.read()      # ...and read from the pipe.  This should include anything that came from the stdout of
                                                     #     the child process.  Since we closed the input side of the pipe, the `read()` 
                                                     #     will read an EOF after all the data in the pipe is returned.

print child_process_output                           # Win! (`write()`ing out the parent stdout want we found in the pipe from the child proc