1

For instance, if I want to run from a shell script something in python I would do:

    echo 'print("hello world")' | python2

Now imagine I want to run something interactive, i.e., it needs my input, for instance

    name = raw_input("Enter your name:")

By doing:

    echo 'name = raw_input("Enter your name:")' | python2

I get an error. So how is the correct way to run something from bash but that I can interact with?

PS - this is simply an example. I need to run a set of scripts in a different external program (not python), and some of these scripts need some input from the user. Everything runs smoothly until the point when there is the need for input, where the current script crashes and passes to the next.

Thanks :)

3 Answers 3

1

When you pipe a program as a string to a command interpreter, the interpreter reads it from its standard input, which is therefore bound to the reading end of the pipe: commands that try reading interactively provided information from it will generally receive an EOF.

For instance:

$ echo 'read x; echo "$x"' | bash
  # Just prints an empty line, doesn't wait for input

To make this work you need to either:

  • rewrite the script to make it read the interactively provided input from a different file descriptor, created as a duplicate of the standard input of the process that runs the whole pipeline:

    { echo '0<&3 read x; echo "$x"' | bash; } 3<&0
    

    In the Python case (for illustrative purposes only, I am not a Python programmer):

    { echo 'import sys; \
      sys.stdin = open("/dev/fd/3"); \
      name = raw_input("Enter your name:")' | python2
    } 3<&0
    

    Or

  • make the interpreter read from a different file descriptor, bound to the reading and of the pipe, and do a bit of file descriptor shuffling to make the standard input from the process that runs the whole pipeline available to it. Assuming the interpreter accepts the script as an argument and your platform supports the /dev/fd/n way of accessing file descriptors:

    { echo 'read x; echo "$x"' | bash /dev/fd/3 3<&0 0<&4; } 4<&0
    

    In the case of your sample Python script:

    { echo 'name = raw_input("Enter your name:")' |
      { python2 /dev/fd/3
      } 3<&0 0<&4
    } 4<&0
    

    Or

  • if your external program does not accept a script as an argument (neither as a string as in bash -c 'echo "foo"' nor as a file name as in bash /dev/fd/3) and needs to read both the script and any interactively provided input from standard input, you may be able to use something along these lines:

    { echo 'echo start; echo "foo? "; read x; echo "$x"; echo end' |
      bash -c '0<&3 cat -
        while IFS= read -r x
        do
          printf "%s\n" "$x"
        done' 3<&0 0<&4 |
      bash
    } 4<&0
    
1
0

Simple code (hope you get it):

read i && echo $i|python3
1
  • ... why not provide some explanation? In its current state, the automatic quality control placed this answer in my "Low quality posts" review queue ...
    – AdminBee
    Oct 21, 2020 at 7:16
0

Pass the script as argument instead of stdin:

python2 -c 'name = raw_input("Enter your name:")'

Note that nothing stops you from putting that script on several lines like:

python2 -c '

# my fancy inline script
name = raw_input("Enter your name:")

'

(but beware that for the python code to contain a ', you'll need to enter it as '\'').

The interpreter -c <code> syntax is common to a few interpreters. For instance, that's how shells are run most of the time (system("some shell code") actually runs sh -c "some shell code"). Some other interpreters (sed / perl / gawk use -e (for expression) instead of -c (code / command)).

(that's limited to short scripts (not larger than 128KiB on Linux).

Or via a different file descriptor and a here-document:

python2 /dev/fd/3 3<< 'EOF'
name = raw_input("Enter your name:")
EOF

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.