7

I'd like to pipe the output of ssh to a different command. For example:

ssh myserver cat remote-file | diff local-file -

The problem is that myserver asks for a password, but I can't enter it. (For some reason, public-key authentication is not available on the server. I can't change this.) I get the "Password:" prompt, but the keys I type are echoed, and not passed to ssh. How can I make ssh get the password?

Note I'm not trying to pipe the password into ssh. I'm trying to pipe the output of ssh, and enter the password as usual.

In case it matters, I'm using bash on OS X 10.7 (Lion), and the standard Terminal. I have no aliases set up that could cause this. I've seen the same problem different (Linux) systems, so I believe it's not specific to my setup.

  • 1
    Do you not get the same if you run ssh myserver cat remote-file alone? What do you see when you run that after set -x? – Stéphane Chazelas Aug 18 '13 at 16:06
  • Do you see test when you echo test > /dev/tty? – Stéphane Chazelas Aug 18 '13 at 16:10
  • @StephaneChazelas: Thanks, your comments helped me figure out the answer! If I run ssh myserver cat remote-file alone it works as expected, echo test > /dev/tty works as well. However, running with set -x spits out a bunch of junk. It turns out that I had customized my prompt to set the window title to the currently running command. That created a subshell for every command, and somehow messed this up. When I commented out the offending lines, it worked. – jdm Aug 18 '13 at 16:27
4

OK, it turns out this was due to a bizarre interaction with my bash configuration.

I have something in my .bash_profile that puts the currently executed command into the window title, or the screen tab. It works by using a trap:

trap 'bash_current_command' DEBUG

and before that:

function bash_current_command {
    # only works in bash > 3.1
    #set -- $BASH_COMMAND

    # for old bash
    set -- $(history 1)
    shift

    if [[ "$1" == "sudo" ]]; then
        cmd="*$(basename -- "$2")*"
    else
        cmd="$(basename -- "$1")"
    fi

    bash_set_title "$cmd"
}

bash_set_title is a little function that sets the current terminal's title and icon title using ANSI escape codes. As you can see, this creates subshells $(...), and my intuition told me this could be the problem. Indeed, after I changed those lines, it worked!

If someone knows why this happens, I'd be glad to hear details. Do subshells generally steal tty input? Or is it only a problem in a debug trap? I don't recall problems with regular stdin input / piping into commands.

(As you can also see, there are a few problems with my function anyway - it uses one of the subshells as a workaround so it can run on an archaic bash version (I'm stuck with it on a legacy system, but still wanted a unified configuration). The other subshell is used to extract the actual command when using sudo, but this fails if sudo is called with switches like sudo -H -u user command. So I'll regard this as an opportunity to fix up this code...)

| improve this answer | |
  • That sounds like a bug in bash. With 3.0.16, I don't reproduce yours, but I see bash: child setpgid (24993 to 24990): Operation not permitted upon running basename. My suspicion is that ssh ends up being not in the foreground process group of the terminal and is therefore being suspended when it tries to disable echo, which is why you can see your echoed password. The log of a strace -f would tell us more. – Stéphane Chazelas Aug 18 '13 at 19:29
2

This answer doesn't explain your specific issue but offers some possible workarounds that are also likely to make your life more convenient.

If you're using ssh only to access remote files and not to run remote commands, then you can mount the remote filesystem through sshfs. You'll need to install FUSE for OS X and SSHFS first. (I don't know if there are binary distributions for OSX.) Then run

mkdir ~/myserver
sshfs myserver:/ ~/myserver

You'll need to authenticate only when you run the sshfs command. After that, the remote files are available under ~/myserver, so you can do diff ~/myserver/path/to/remote-file local-file without having to worry that one of the files is remote. Run fusermount -u ~/myserver to unmount the filesystem.

Another approach that lets you authenticate once and then run many ssh commands is to set up a master connection. See Reusing ssh session for repeated rsync commands

| improve this answer | |

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.