2

For example:

A script1.py gets an infix expression from the user and converts it to a postfix expression and returns it or prints it to stdout

script2.py gets a postfix expression from stdin and evaluates it and outputs the value

I wanted to do something like this:

python3 script1.py | python3 script2.py

This doesn't work though, could you point me in the right direction as to how I could do this?

EDIT -

here are some more details as to what "doesn't work".

When I execute python3 script1.py | python3 script2.py the terminal asks me for input for the script2.py program, when it should be asking for input for the script1.py program and redirecting that as script2.py's input.

So it asks me to "Enter a postfix expression: ", when it should be asking "Enter an infix expression: " and redirect that to the postfix script.

7alman
  • 157
  • 2
  • 3
  • 11
  • what's wrong if you put them as function and just call them in sequence. your way should work btw, the problem must be your de/serialization. – Jason Hu Jun 15 '16 at 23:04
  • 1
    That command line should do exactly what you say you want. How does it fail to meet your expectations, *precisely*? ("It doesn't work" is a totally information-free problem report.) – rici Jun 15 '16 at 23:09
  • Nothing's wrong with me defining the scripts as two separate functions and calling them in sequence, but I want to learn how to do it this way. As for the de/serialization, could you elaborate on that? – 7alman Jun 15 '16 at 23:09
  • @rici I gave more details on the issue, hope that helps. – 7alman Jun 15 '16 at 23:14
  • 3
    @7alman: Think about what is going on. You are redirecting the output of the first script into the second script. So if your first script outputs "Enter an infix expression", where do you think that output will go??? (And how silly is it for the second script to prompt when the input is coming from another script, not a human being?) This is why scripts should not prompt. If you insist on prompting, you should send the prompt to stderr, not stdout. – rici Jun 15 '16 at 23:38
  • Take a look at [`os.isatty`](https://docs.python.org/2/library/os.html#os.isatty). You can use that to tell is input is from a terminal or pipe, and only display a prompt for a terminal. – Barmar Jun 15 '16 at 23:40
  • @rici That was the problem... Thank you rici appreciate it and thanks to everyone else who answered. – 7alman Jun 15 '16 at 23:47

3 Answers3

1

If I undestand your issue correctly, your two scripts each write out a prompt for input. For instance, they could both be something like this:

in_string = input("Enter something")
print(some_function(in_string))

Where some_function is a function that has different output depending on the input string (which may be different in each script).

The issue is that the "Enter something" prompt doesn't get displayed to the user correctly when the output of one script is being piped to another script. That's because the prompt is written to standard output, so the first script's prompt is piped to the second script, while the second script's prompt is displayed. That's misleading, since it's the first script that will (directly) receive input from the user. The prompt text may also mess up the data being passed between the two scripts.

There's no perfect solution to this issue. One partial solution is to write the prompt to standard error, rather than standard output. This would let you see both prompts (though you'd only actually be able to respond to one of them). I don't think you can directly do that with input, but print can write to other file streams if you want: print("prompt", file=sys.stderr)

Another partial solution is to check if your input and output streams and skip printing the prompts if either one is not a "tty" (terminal). In Python, you can do sys.stdin.isatty(). Many command line programs have a different "interactive mode" if they're connected directly to the user, rather than to a pipe or a file.

If piping the output around is a main feature of your program, you may not want to use prompts ever! Many standard Unix command-line programs (like cat and grep) don't have any interactive behavior at all. They require the user to pass command line arguments or set environment variables to control how they run. That lets them work as expected even when they don't have access to standard input and standard output.

Blckknght
  • 100,903
  • 11
  • 120
  • 169
1

For completion's sake, and to offer an alternative to using the os module:

The fileinput module takes care of piping for you, and from running a simple test I believe it'll make it an easy implementation.

To enable your files to support piped input, simply do this:

import fileinput
with fileinput.input() as f_input:  # This gets the piped data for you
    for line in f_input:
        # do stuff with line of piped data

all you'd have to do then is:

$ some_textfile.txt | ./myscript.py

Note that fileinput also enables data input for your scripts like so: $ ./myscript.py some_textfile.txt $ ./myscript.py < some_textfile.txt

This works with python's print output just as easily:

>test.py  # This prints the contents of some_textfile.txt
with open('some_textfile.txt', 'r') as f:
    for line in f:
        print(line)

$ ./test.py | ./myscript.py

Of course, don't forget the hashbang #!/usr/bin/env python at the top of your scripts for this way to work.

The recipe is featured in Beazley & Jones's Python Cookbook - I wholeheartedly recommend it.

deepbrook
  • 2,523
  • 4
  • 28
  • 49
0

For example if you have nginx running and script1.py:

import os

os.system("ps aux")

and script2.py

import os

os.system("grep nginx")

Then running:

python script1.py | script2.py

will be same as

ps aux | grep nginx
dmitryro
  • 3,463
  • 2
  • 20
  • 28