6

I wonder how to create a pipe

program 1 | ... | program N

where multiple of the programs ask for user input. The problem is that | starts the programs in parallel and thus they start reading from the terminal in parallel.

For such cases it would be useful to have a pipe | that starts program (i+1) only after program i has produced some output.

Edit:

Example:

cat /dev/sda | bzip2 | gpg -c | ssh user@host 'cat > backup'

Here both gpg -c as well as ssh ask for a password.

A workaround for this particular example would be the creation of ssh key pairs, but this is not possible on every system, and I was wondering whether there is a general solution. Also gpg allows for the passphrase to be passed as command line argument, but this is not suggested for security reasons.

  • 1
    Once you connect a program to a pipe, it gets its input from another program, not the user. Are you sure you want a pipe? I can't really think of a program that both takes user input *and* reads from standard input. – chepner Aug 02 '12 at 22:32
  • Not an answer, but an idea which may point you in the right direction: Could you include code around each program to check the output for a particular string, updating a flag once found and have the other calls loop until the previous program's flag were updated, then allow them to run? – JohnLBevan Aug 02 '12 at 22:53
  • 2
    Show a specific example of this happening. A program could read from `stdin` and from `/dev/tty`, but this would be unusual. – Dennis Williamson Aug 03 '12 at 01:01
  • 1
    @chepner: no, not always, program can read from `tty` also. And the case in the question is exact such case. – Igor Chubin Aug 03 '12 at 08:16

4 Answers4

1

You can use this construction:

(read a; echo "$a"; cat) > file

For example:

$ (read a; echo "$a"; echo cat is started > /dev/stderr; cat) > file
1
cat is started
2
3

Here 1, 2 and 3 were entered from keyboard; cat is started was written by echo.

Contents of file after execution of the command:

$ cat file
1
2
3
Igor Chubin
  • 61,765
  • 13
  • 122
  • 144
1

I am now using:

#!/bin/bash
sudo echo "I am root!"
sudo cat /dev/disk0 | bzip2 | gpg -c | (read -n 1 a; (echo -n "$a"; cat) | ssh user@host 'cat > backup')

The first sudo will prevent the second from asking the password again. As suggested above, the read postpones the starting of ssh. I used -n 1 for read since I don't want to wait for newline, and -n for echo to surpress the newline.

Qix - MONICA WAS MISTREATED
  • 14,451
  • 16
  • 82
  • 145
  • 1
    `sudo -v` will extend the timeout, prompting for the password if you haven't been validated at all yet. This lets you avoid having to come up with an extraneous command (`echo "I am root!"`) to run. – chepner Feb 26 '13 at 17:26
0

for one you can give gpg the password with the --passphrase option.

For ssh the best solution would be to login by key. But if you need to do by password the expect command will be good. Here's a good example: Use expect in bash script to provide password to SSH command

Expect also allows you to have some input - so if you don't want to hardcode your passwords this might be the way to go.

Community
  • 1
  • 1
bdecaf
  • 4,652
  • 23
  • 44
0

I've needed something similar a few times before, where the first command in the pipeline requires a password to be entered, and the next command doesn't automatically cater for this (like the way that less does).

Similar to Igor's response, I find the use of read inside a subshell useful:

cmd1 | ( read; cat - | cmd2 )
curious_prism
  • 349
  • 1
  • 5