Last active
May 5, 2025 12:16
-
-
Save mamaj/a7b378a5c969e3e32a9e4f9bceb0c5eb to your computer and use it in GitHub Desktop.
Python: simple class to perform commands and copy files (scp) on ssh using subprocess and native ssh client (OpenSSH).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import subprocess | |
import os | |
from pathlib import Path | |
from typing import Union | |
class SshClient(): | |
""" Perform commands and copy files on ssh using subprocess | |
and native ssh client (OpenSSH). | |
""" | |
def __init__(self, | |
user: str, | |
remote: str, | |
key_path: Union[str, Path]) -> None: | |
""" | |
Args: | |
user (str): username for the remote | |
remote (str): remote host IP/DNS | |
key_path (str or pathlib.Path): path to .pem file | |
""" | |
self.user = user | |
self.remote = remote | |
self.key_path = str(key_path) | |
def cmd(self, | |
cmds: list[str], | |
check=True, | |
strict_host_key_checking=False, | |
**run_kwargs) -> subprocess.CompletedProcess: | |
"""runs commands consecutively, ensuring success of each | |
after calling the next command. | |
Args: | |
cmds (list[str]): list of commands to run. | |
strict_host_key_checking (bool, optional): Defaults to True. | |
""" | |
strict_host_key_checking = 'yes' if strict_host_key_checking else 'no' | |
cmd = ' && '.join(cmds) | |
return subprocess.run( | |
[ | |
'ssh', | |
'-i', self.key_path, | |
'-o', f'StrictHostKeyChecking={strict_host_key_checking}', | |
'-o', 'UserKnownHostsFile=/dev/null', | |
'-o', 'LogLevel=ERROR', | |
f'{self.user}@{self.remote}', | |
cmd | |
], | |
check=check, | |
**run_kwargs | |
) | |
def scp(self, | |
sources: list[Union[str, bytes, os.PathLike]], | |
destination: Union[str, bytes, os.PathLike], | |
check=True, | |
strict_host_key_checking=False, | |
recursive=False, | |
**run_kwargs) -> subprocess.CompletedProcess: | |
"""Copies `srouce` file to remote `destination` using the | |
native `scp` command. | |
Args: | |
source (Union[str, bytes, os.PathLike]): List of source files path. | |
destination (Union[str, bytes, os.PathLike]): Destination path on remote. | |
""" | |
strict_host_key_checking = 'yes' if strict_host_key_checking else 'no' | |
return subprocess.run( | |
list(filter(bool, [ | |
'scp', | |
'-i', self.key_path, | |
'-o', f'StrictHostKeyChecking={strict_host_key_checking}', | |
'-o', 'UserKnownHostsFile=/dev/null', | |
'-o', 'LogLevel=ERROR', | |
'-r' if recursive else '', | |
*map(str, sources), | |
# sources, | |
f'{self.user}@{self.remote}:{str(destination)}', | |
])), | |
check=check, | |
**run_kwargs | |
) | |
def validate(self): | |
return self.cmd([f'echo " "'], check=False).returncode == 0 | |
def ssh_connect_cmd(self) -> str: | |
return f'ssh -i {self.key_path} {self.user}@{self.remote}' | |
Is there a way to pass in the user's password and not use the key ?
Not with the native ssh command. Probably need to install sshpass on the remote and modify client.cmd function accordingly.
Thanks
Thanks for this gist, I got the link to here from Stackoverflow.
I pretty much prefer your tiny SSH client code rather than importing an entire module just for running something remotely via ssh.
Well done!
For my project I removed the key_path
as I deploy the ssh key in the standard user directory.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
usage: