2

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?

arod
  • 11,893
  • 5
  • 25
  • 33
  • @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.”* – spectras Sep 02 '17 at 01:57
  • @DYZ adding `stdin=sys.stdin` doesn't seem to do anything... still no chance to type input – arod Sep 02 '17 at 02: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) – spectras Sep 02 '17 at 02: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. – arod Sep 02 '17 at 03:52

3 Answers3

3

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.

arod
  • 11,893
  • 5
  • 25
  • 33
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.

Brian Tiffin
  • 3,718
  • 1
  • 20
  • 34
  • actually `myscript.sh` was an example, it is really an absolute path `/myscript.sh`... still doesn't work. I corrected the question. – arod Sep 02 '17 at 03:26
  • 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. – Brian Tiffin Sep 02 '17 at 03:48
  • 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. – arod Sep 02 '17 at 03:52
  • 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. – Brian Tiffin Sep 02 '17 at 04:00
0

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)
Tano
  • 33
  • 5