打印Python subprocess.Popen执行的命令

6 投票
2 回答
7528 浏览
提问于 2025-04-17 12:30

我有一个脚本,它可以自动在多个git仓库中修改作者信息。

def filter_history(old, new, name, repoPath):

command = """--env-filter '
        an="$GIT_AUTHOR_NAME"
        am="$GIT_AUTHOR_EMAIL"
        cn="$GIT_COMMITTER_NAME"
        cm="$GIT_COMMITTER_EMAIL"

        if [[ "$GIT_COMMITTER_NAME" = "|old|" ]]
        then
            cn="|name|"
            cm="|new|"
        fi

        if [[ "$GIT_AUTHOR_NAME" = "|old|" ]]
        then
            an="|name|"
            am="|new|"
        fi

        export GIT_AUTHOR_NAME="$an"
        export GIT_AUTHOR_EMAIL="$am"
        export GIT_COMMITTER_NAME="$cn"
        export GIT_COMMITTER_EMAIL="$cm"
'
"""

#DO string replace
command = command.replace("|old|", old)
command = command.replace("|new|", new)
command = command.replace("|name|", name)

print "git filter-branch -f " + command

process = subprocess.Popen(['git filter-branch -f', command],cwd=os.path.dirname(repoPath), shell=True)
process.wait()

这个命令执行得很好,但告诉我在仓库历史中没有任何变化。不过,如果我把打印出来的命令(应该就是正在执行的命令)放到一个shell脚本里执行,它就能正常改变历史。我觉得这个命令可能没有被正确执行。有没有办法让我看到子进程模块到底执行了什么命令呢?

2 个回答

2

如果你的应用程序在Windows环境下运行,正如下面这个回答所说的,subprocess模块有一个不太为人所知的功能,叫做subprocess.list2cmdline,你可以用它。这个subprocess.list2cmdline可以把一系列的参数转换成命令行字符串,使用的规则和微软的C运行时是一样的。

如果你使用的是Python 3.3以上的版本,你还可以直接通过subprocess对象获取参数列表,方法是使用.args

import subprocess

process = subprocess.Popen(...)
subprocess.list2cmdline(process.args)

从Python 3.8开始,你还可以使用shlex.join()这个函数:

不过要记住,subprocess是通过进程间通信(IPC)来工作的,所以最好的方法就是直接查看args列表,因为这些参数会被传递给被调用程序的argv

5

当你使用 shell = True 时,subprocess.Popen 会期待第一个参数是一个字符串。如果可以的话,最好不要使用 shell = True,因为这样可能会带来 安全风险(请查看警告)

如果你不使用 shell = True,或者使用 shell = False,那么 subprocess.Popen 就会期待一个参数列表。你可以通过 shlex.split 从一个字符串生成这个参数列表:

import shlex
import subprocess

def filter_history(old, new, name, repoPath):
    """Change author info
    """
    # http://help.github.com/change-author-info/
    # http://stackoverflow.com/a/3880493/190597
    command = """git filter-branch -f --env-filter '
        an="$GIT_AUTHOR_NAME"
        am="$GIT_AUTHOR_EMAIL"
        cn="$GIT_COMMITTER_NAME"
        cm="$GIT_COMMITTER_EMAIL"

        if [[ "$GIT_COMMITTER_NAME" = "{old}" ]]
        then
            cn="{name}"
            cm="{new}"
        fi

        if [[ "$GIT_AUTHOR_NAME" = "{old}" ]]
        then
            an="{name}"
            am="{new}"
        fi

        export GIT_AUTHOR_NAME="$an"
        export GIT_AUTHOR_EMAIL="$am"
        export GIT_COMMITTER_NAME="$cn"
        export GIT_COMMITTER_EMAIL="$cm"
      '
      """.format(old = old, new = new, name = name)

    process = subprocess.Popen(
        shlex.split(command),
        cwd = os.path.dirname(repoPath))
    process.communicate()

撰写回答