3

I'm doing a GIT hook in Python 3.5. The python script calls a Bash script that that reads input from the user using read command.

The bash script by itself works, also when calling directly the python script, but when GIT runs the hook written in Python, it doesn't work as expected because no user input is requested from the user.

Bash script:

#!/usr/bin/env bash

echo -n "Question? [Y/n]: "
read REPLY

GIT Hook (Python script):

#!/usr/bin/env python3    
from subprocess import Popen, PIPE
proc = Popen('/path/to/myscript.sh', shell=True, stderr=PIPE, stdout=PIPE)        
stdout_raw, stderr_raw= proc.communicate()

When I execute the Python script, Bash's read does not seem to be waiting for an input, and I only get:

b'\nQuestion? [Y/n]: \n'

How to let the bash script read input when being called from Python?

4
  • @DYZ> that should be the default behavior: “With the default settings of None, no redirection will occur; the child’s file handles will be inherited from the parent.” Commented Sep 2, 2017 at 1:57
  • @DYZ adding stdin=sys.stdin doesn't seem to do anything... still no chance to type input Commented Sep 2, 2017 at 2:07
  • @arod> Copy-pasted your code, and works fine here, provided . is in $PATH (that's a bad idea though you should not do that) Commented Sep 2, 2017 at 2:10
  • You we're right that when invoking directly the script it worked OK. I updated the question adding the detail that the python script is called from a GIT hook. Commented Sep 2, 2017 at 3:52

4 Answers 4

4

It turns out the problem had nothing to do with Python: if the GIT hook called a bash script it also failed to ask for input.

The solution I found is given here.

Basically, the solution is to add the following to the bash script before the read:

# Allows us to read user input below, assigns stdin to keyboard
exec < /dev/tty

In my case, I also had to call the bash process simply like Popen(mybashscript) instead of Popen(mybashscript, shell=True, stderr=PIPE, stdout=PIPE)), so the script can freely output to STDOUT and not get captured in a PIPE.

Alternatively, I didn't modify the bash script and instead used in Python:

sys.stdin = open("/dev/tty", "r")
proc = Popen(h, stdin=sys.stdin)

which is also suggested in the comments of the aforementioned link.

Sign up to request clarification or add additional context in comments.

Comments

1

Adding

print(stdout_raw)
print(stderr_raw)

Shows

b''
b'/bin/sh: myscript.sh: command not found\n'

here. Adding ./ to the myscript.sh worked for the READ once python could find the script. cwd='.' in Popen may also work.

4 Comments

actually myscript.sh was an example, it is really an absolute path /myscript.sh... still doesn't work. I corrected the question.
Hmm, have you tried another form of reading from stdin, other than bash? This seems curious. Like spectras commented, the code works fine here (Fedora 24, Python 3.5.3). I tried with a little C fgets test, that worked too.
you we're right that when invoking directly the script it worked OK. I updated the question adding the detail that the python script is called from a GIT hook.
That changes up the testing then. I'm not totally up on git hooks, but the docs mention that at least pre-push and post-rewrite pass data to target stdin. You may be competing/conflicting with redirections here, @arod. git may only be passing empty data and a newline which satisfies the read. Aside: you don't need the explicit REPLY after read in bash, but I don't think it hurts.
1

This is what worked for me without invoking a bash script from within python. It is a modified version from arod's answer.

    import subprocess
    import sys

    sys.stdin = open("/dev/tty", "r")
    user_input = subprocess.check_output("read -p \"Please give your input: \" userinput && echo \"$userinput\"", shell=True, stdin=sys.stdin).rstrip()

    print(user_input)

Comments

0

Based on the above replies:

import sys
import subprocess

def getInput(prompt):
    sys.stdin = open("/dev/tty", "r")
    command = f"read -p \"{prompt}\" ret && echo \"$ret\""
    userInput = subprocess.check_output(command, shell=True, stdin=sys.stdin).rstrip().decode("utf-8")
    return userInput

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.