据我所知,有三种方法可以在Python中执行系统命令:
os.system(command) -> exit_status
os.popen(command [, mode='r' [, bufsize]]) -> pipe
commands.getoutput(command)-> string
现在我需要控制一个系统命令的excutor,旁边的方式如下:
os.system('su xxx;' + command)
有没有其他更优雅的方式来达到相同的效果?
你所提到的所有东西(顺便说一下子进程模块已经成功了)是产卵过程的方式。 你听起来像是在寻找setuid
。 你可以调用一个可以实现这个功能的函数(比如os.setuid
),或者,根据你的脚本的实际情况,你可以像升级用户一样运行整个脚本。
使用subprocess
模块(实现@ Julian的建议 )以不同的用户身份运行一个命令。 它类似于@ tMC的代码,但在更高层次上:
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
字典以设置子流程的环境变量。
虽然对于大多数实现来说可能是“低层次”的,但对于某些人来说,理解在更高级别的模块中是如何发生的,也许是教育的。
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