Using back-ticks in Python subprocess

I want to run this git command through a Python script and get the output of it:

git diff --name-only mybranch `git merge-base mybranch develop`

The purpose of the command is to see what changes have been made on mybranch since the last merge with develop.

  • quitting git log from a bash script
  • Silence git (no errors, no output, don't say a word)
  • Git Autocompletion looks weird (and only half-functional)
  • How to get the string between two dots in bash?
  • git-filter-branch to remove strings, but where strings contain $ ' \ and other characters
  • pg_dump not been killed inside script with kill command
  • To achieve this I’m using subprocess.Popen:

    output = subprocess.Popen(["git", "diff", "--name-only", "mybranch", "`git merge-base mybranch develop`"], stdout=subprocess.PIPE, shell=True)
    

    However, this does not work. The variable output.communicate()[0] simply gives me a printout of git usage — essentially telling me the input command is wrong.

    I saw that a similar question exists here, but it only told me to use shell=True which didn’t solve my problem.

    I also attempted to run the two commands in succession, but that gave me the same output as before. It is possible that I am missing something in this step, though.

    Any help or tips are appreciated.

  • Why doesn't my Git pre-commit hook trigger in a cloned repository?
  • Branch descriptions in git
  • Using a different application to colourize diff output in git
  • can't add git submodule to repository
  • How does Xcode work with Git for changing branch and checkout older commit?
  • git fetch fails due to pack-object failure
  • 2 Solutions collect form web for “Using back-ticks in Python subprocess”

    Backticks and subprocess

    The backtick being a shell feature, you may not have a choice but to use shell=True, however pass in a shell command string, not a list of args

    So for your particular command (assuming it works in the first place)

    process = subprocess.Popen("git diff --name-only mybranch `git merge-base mybranch develop`", stdout=subprocess.PIPE, shell=True)
    

    Notice when you call Popen() you get a process, shouldn’t be called output IMO

    Here’s a simple example that works with backticks

    >>> process = subprocess.Popen('echo `pwd`', stdout=subprocess.PIPE, shell=True)
    >>> out, err = process.communicate()
    >>> out
    '/Users/bakkal\n'
    

    Or you can use the $(cmd) syntax

    >>> process = subprocess.Popen('echo $(pwd)', stdout=subprocess.PIPE, shell=True)
    >>> out, err = process.communicate()
    >>> out
    '/Users/bakkal\n'
    

    Here’s what did NOT work (for backticks)

    >>> process = subprocess.Popen(['echo', '`pwd`'], stdout=subprocess.PIPE, shell=True)
    >>> out, err = process.communicate()
    >>> out
    '\n'
    >>> process = subprocess.Popen(['echo', '`pwd`'], stdout=subprocess.PIPE, shell=False)
    >>> out, err = process.communicate()
    >>> out
    '`pwd`\n'
    

    On POSIX, the argument list is passed to /bin/sh -c i.e., only the first argument is recognized as a shell command i.e., the shell runs git without any arguments that is why you see the usage info. You should pass the command as a string if you want to use shell=True. From the subprocess docs:

    On POSIX with shell=True, the shell defaults to /bin/sh. If args is a
    string, the string specifies the command to execute through the shell.
    This means that the string must be formatted exactly as it would be
    when typed at the shell prompt. This includes, for example, quoting or
    backslash escaping filenames with spaces in them. If args is a
    sequence, the first item specifies the command string, and any
    additional items will be treated as additional arguments to the shell
    itself. That is to say, Popen does the equivalent of:

    Popen(['/bin/sh', '-c', args[0], args[1], ...])
    

    You don’t need shell=True in this case.

    #!/usr/bin/env python
    from subprocess import check_output
    
    merge_base_output = check_output('git merge-base mybranch develop'.split(), 
                                     universal_newlines=True).strip()
    diff_output = check_output('git diff --name-only mybranch'.split() +
                               [merge_base_output])
    
    Git Baby is a git and github fan, let's start git clone.