2

When I want to compare a local file with a remote file, any of these usually works:

$ ssh remote cat file | diff file -
$ diff file <(ssh remote cat file)

However, sometimes (especially when the password is needed) they fail like this:

$ ssh remote cat file | diff file -
1,162d0
< ...
< ...
Password: 

Here, diff doesn't wait for ssh to complete and considers the second file empty. It is only after diff is finished that ssh asks for the password, but it is too late.

$ diff file <(ssh remote cat file)
Password: 
Password:       # asking again after a few seconds
#&%Pasword:     # the typed raw password leaks into the terminal
user@remote's password:
Permission denied, please try again.
user@remote's password:
Permission denied, please try again.
user@remote's password:
Received disconnect from XXX.XX.XX.XX: 2: Too many authentication failures for user
1,162d0
< ...
< ...

This time, ssh asks for the password, but the typed password is echoed back to the terminal, and ssh doesn't get it. Finally, ssh fails and diff continues with an empty second file.

Can you explain why these are happening, or what's happening in detail under the hood?

  • 1
    I can't explain or reproduce this behavior. What OS are you running, what shell, what version? If you move your ~/.bashrc, ~/.zshrc or other relevant file out of the way, does it make a difference? P.S. Mount the remote directory with SSHFS, then call diff on the two regular files. – Gilles Jul 27 '12 at 0:50
  • Ubuntu 12.04, bash 4.2.24(1)-release. – musiphil Jul 27 '12 at 5:52
  • It seems that alias diff='colordiff' messed up with the redirection; not using the alias solves the problem for the pipeline case. Thank you! However, the process substitution case remains the same. Even cat <(ssh remote cat file) fails in the same way. – musiphil Jul 27 '12 at 6:02
2

When you run ssh first:

ssh remote 'cat file' | cat

ssh gets your controlling terminal as stdin, so you can type your password in with no problems.

When you use bash's process substitution, stdin remains connected to the first command, and the subprocess's output pipe gets passed to the first command as an argument:

cat <(ssh remote 'cat file')

If the ssh negotiation succeeded, /dev/fd/63 will be a pipe containing the output of ssh remote 'cat file'.

This can be demonstrated with the following command:

$ echo <(ls)
/dev/fd/63

The important part of this is that your terminal is connected to cat's stdin, not ssh's. When this runs, cat gets a command-line argument like /dev/fd/63. Therefore, cat ignores stdin altogether; however, it is still connected to the cat process, and not ssh, so your password goes nowhere.

If you want to change this, you will need to run ssh first, and pipe the output to diff, as you have already demonstrated above:

ssh remote 'cat file' | diff file -

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.