0

I'm trying to execute a python script(machine learning) via remote server which has good resources. And I'm trying to call remote python code in local environment with Paramiko library. (I send command to remote server, and that remote server will execute that command)

code in local environment is below:

cli = paramiko.SSHClient()
cli.set_missing_host_key_policy(paramiko.AutoAddPolicy)
server = config['ip']
user = 'ubuntu'
pwd = config['pw']
cli.connect(server, username=user, password=pwd)
command="python my_code.py"
stdin, stdout, stderr = cli.exec_command(command)

I send a command to remote server via above code. But, that doesn't work. When the command is basic linux command (ex, ls -al, cd, pwd ...) those work. But the remote server's cell does not recognize other commands like, python ~.py, conda activate, pip install...

My wonder is that, those commands are available when I directly connect to remote server's shell. see below:

python command works

pip command works

I guess the bash that is opened when I directly connect is different from the bash that Paramiko handles. How can I handles this problem? thanks sincerely

임승원
  • 67
  • 7

1 Answers1

2

Source of the issue:

There are different types of shell sessions that get run depending on how you connect to a remote (or local) machine. These different shell types "source" (or execute) different configuration files when they are launched. When you ssh into the remote server from your terminal, you connect to what's called an "interactive login shell"—it's "interactive" because you can interactively run and interrupt commands, and it's a "login shell" because it's the first shell launched after you logged in1.

Interactive Bash login shells source various files that typically (among other things) make additional commands available to you by adding directories containing executables to your $PATH. You mentioned conda, for example—when you install conda, it automatically adds some code to your .bash_profile to make it available when that file is sourced, which it is for interactive login shells2.

However, when you send a command over ssh the way you're doing with paramiko, that command is by default invoked in a noninteractive (non-login) shell. In this case, your configuration files are not sourced3, which is why the commands you expect to be available are not.

How to fix it:

The easiest way would be to simply change cli.exec_command(<command>) to cli.exec_command('/bin/bash -lc "<command>"'). The -l flag causes bash to behave as though it were invoked in a login shell, meaning it will source all the same configuration files it does when you ssh in from your terminal.

If you need an interactive shell, you could instead use cli.get_pty() and cli.invoke_shell(), but this isn't necessary for just running a Python script.

Finally, I highly recommend taking a look at spurplus. It's built on top of spur, which is in turn built on top of paramiko, and it adds some nice extra features in addition to being much easier to use.


1this is true for Linux systems. On MacOS, subsequent interactive shells you launch are also login shells.

2there are also interactive non-login shells, which instead source your .bashrc. Again, some of this is slightly different for non-Linux machines, as well as other shells like Zsh.

3unless you've specified a $BASH_ENV file.

paxton4416
  • 495
  • 3
  • 7