How to Pipe Initial Input into Process Which Will Then Be Interactive

How can I pipe initial input into process which will then be interactive?

You don't need to write a new tool to forward stdin - one has already been written (cat):

(echo "initial command" && cat) | some_tool

This does have the downside of connecting a pipe to some_tool, not a terminal.

How to pipe data to interactive bash script and pipe output to another command?

Thanks to 4ae1e1, I figured out how to do what I want - specifically, how to get my select_from_list routine to work:

So now I can do something like this:

ls /tmp/ | select_from_list | xargs cat

to choose a file from /tmp and cat it.

select_from_list

#!/bin/bash
prompt="Please select an item:"

options=()

if [ -z "$1" ]
then
# Get options from PIPE
input=$(cat /dev/stdin)
while read -r line; do
options+=("$line")
done <<< "$input"
else
# Get options from command line
for var in "$@"
do
options+=("$var")
done
fi

# Close stdin
0<&-
# open /dev/tty as stdin
exec 0</dev/tty

PS3="$prompt "
select opt in "${options[@]}" "Quit" ; do
if (( REPLY == 1 + ${#options[@]} )) ; then
exit

elif (( REPLY > 0 && REPLY <= ${#options[@]} )) ; then
break

else
echo "Invalid option. Try another one."
fi
done
echo $opt

How to pipe two inputs to the same command?

You can use process substitution:

convert <(cat file1.txt) <(cat file2.txt)

How do I accept piped input and then user-prompted input in a Python script?

I suspect you're out of luck, at least for any kind of cross-platform solution. Python uses sys.stdin for raw_input(), and if you invoke Python so that sys.stdin is on the receiving end of a pipe, Python can't do anything to magically change sys.stdin to the terminal when the piped input ends.

Here's a variant of the question with a Unix-specific workaround as the accepted answer. That cleverly worms around some (not all) of the problem by changing the way the program is invoked.

Sorry.

One way

This seems to work fine for Windows:

import sys
print len(sys.stdin.read()) # anything to consume piped input
sys.stdin = open("CON:", "r")
x = raw_input("sdfklj ")

That is, after reading the piped-in input, sys.stdin is rebound to the special file CON: (which is what Windows calls a DOS box) opened in read mode.

See your Unix docs for what to try there - perhaps /dev/tty1? There are mounds of terminal control options you may need to fiddle with too, depending on platform specifics. That's why I said (at the start) that I think you're out of luck for any cross-platform solution. Python has no special support for terminal devices; i.e., you're on your own for that.

Interactive shell using standard input

Method 1: Get user input via stderr

The problem is stdin for the shell is the cat process and, apparently, you would like the question the user. One way to do that is:

$ cat hoge.sh
#!/bin/sh
read yn <&2
echo "yn is $yn"

This tells read to get its stdin from the stderr, not the cat process. Since stderr has not been redirected, it is still the terminal

In operation, it looks like this:

$ cat hoge.sh | sh
y
yn is y

Method 2: Get user input directly from the terminal

$ cat hoge.sh
#!/bin/sh
read yn </dev/tty
echo "yn is $yn"

When done this way, the read command will get its input from the terminal and any attempt at I/O redirection will be ignored. This can be an advantage or disadvantage depending on your requirements.

How do I push a string to stdin? Provide input via stdin on startup, then read stdin input interactively

This might work:

(echo "something"; cat -) | ./my_program

It creates a sub-shell where the first line of output comes from echo and the rest comes from the standard input to cat, which is the terminal (or the script's standard input, at any rate). I use the - to emphasize that cat is required to read from standard input — it is not simply that I've forgotten to specify "$@" or something after the cat command. Omitting the - doesn't make an operational difference; it might make a comprehensibility difference.

Be aware that the input to my_program is no longer a terminal but a pipe connected to the terminal, which can affect the behaviour of the program. The cat process introduces delays, too.

Expect

If that doesn't do the job, then you probably need to use expect instead. This is a general purpose tool for scripting interactions with other programs, and it uses pseudo-ttys (ptys) to make it appear as if a user at a terminal is communicating with the other program.
As anishsane notes, expect has an interact command that can be used to leave you with the user typing to the program after some fixed preamble.

Beware

Adapting this to your second scenario was not a comfortable experience:

(echo date; cat -) | bash -i

The -i tells Bash it is an interactive shell. The date worked, and I got a prompt after it, but I didn't get any more commands executed. Interrupt got me more prompts; pretty much everything else seemed to be ignored. That may be cat doing too much buffering of its output.

I ended up killing that shell from another terminal window. I had better luck with:

(echo date; cat -) | bash

but there were no prompts from Bash. Be careful; make sure you know how to get out of trouble.

I/O Redirection Stunts

rici also pointed out that in this particular case, you could use:

{ { echo date; echo 'exec 0<&3-';} | bash -i; } 3<&0

That's rather clever because the trailing 3<&0 makes a copy of the original standard input (file descriptor 0) on descriptor 3, and then runs bash -i with its input coming from two echo statements. The first requests the date. The second re-redirects things so that the standard input now comes from file descriptor 3 (that's the 0<&3 part) — which is the original standard input, aka 'the terminal' — and closes file descriptor 3 too (that's the trailing -; it is a Bash extension over POSIX shell I/O redirection).



Related Topics



Leave a reply



Submit