Skip to content

Instantly share code, notes, and snippets.

@mamaj
Last active August 25, 2025 01:00
Show Gist options
  • Save mamaj/a7b378a5c969e3e32a9e4f9bceb0c5eb to your computer and use it in GitHub Desktop.
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).
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}'
@mamaj
Copy link
Author

mamaj commented Aug 25, 2022

Feedbacks are welcome!

@mamaj
Copy link
Author

mamaj commented Aug 25, 2022

usage:

from ssh_utils import SshClient

client = SshClient(user='username', remote='remote_addr', key_path='/path/to/key.pem')

# run a list of commands
client.cmd(['mkdir ~/testdir', 'ls -la', 'echo done!'])

# copy files/dirs
client.scp('my_file.txt', '~/testdir')

@linuxguy123
Copy link

Is there a way to pass in the user's password and not use the key ?

@mamaj
Copy link
Author

mamaj commented Jan 1, 2023

Not with the native ssh command. Probably need to install sshpass on the remote and modify client.cmd function accordingly.

@linuxguy123
Copy link

Thanks

@larsblumberg
Copy link

larsblumberg commented Dec 11, 2024

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.

@istockmarket
Copy link

Skip to content

Search Gists
Search...
All gists
Back to GitHub
@mamaj
mamaj/ssh-utils.py
Last active 2 months ago • Report abuse
Code
Revisions
10
Stars
5
Forks
1
Clone this repository at <script src="https://gist.github.com/mamaj/a7b378a5c969e3e32a9e4f9bceb0c5eb.js&quot;&gt;&lt;/script>

<script src="https://gist.github.com/mamaj/a7b378a5c969e3e32a9e4f9bceb0c5eb.js"></script>

Python: simple class to perform commands and copy files (scp) on ssh using subprocess and native ssh client (OpenSSH).
ssh-utils.py
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}'

@mamaj
Author
mamaj commented on Aug 25, 2022
Feedbacks are welcome!

@mamaj
Author
mamaj commented on Aug 25, 2022
usage:
from ssh_utils import SshClient

client = SshClient(user='username', remote='remote_addr', key_path='/path/to/key.pem')

run a list of commands

client.cmd(['mkdir ~/testdir', 'ls -la', 'echo done!'])

copy files/dirs

client.scp('my_file.txt', '~/testdir')
@linuxguy123
linuxguy123 commented on Jan 1, 2023
Is there a way to pass in the user's password and not use the key ?

@mamaj
Author
mamaj commented on Jan 2, 2023
Not with the native ssh command. Probably need to install sshpass on the remote and modify client.cmd function accordingly.

@linuxguy123
linuxguy123 commented on Jan 3, 2023
Thanks

@larsblumberg
larsblumberg commented on Dec 12, 2024
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.

@istockmarket
Comment

Leave a comment

Footer
© 2025 GitHub, Inc.
Footer navigation
Terms
Privacy
Security
Status
Docs
Contact
Manage cookies
Do not share my personal information

@istockmarket
Copy link

Skip to content

Search Gists
Search...
All gists
Back to GitHub
@mamaj
mamaj/ssh-utils.py
Last active 2 months ago • Report abuse
Code
Revisions
10
Stars
5
Forks
1
Clone this repository at <script src="https://gist.github.com/mamaj/a7b378a5c969e3e32a9e4f9bceb0c5eb.js&quot;&gt;&lt;/script>

<script src="https://gist.github.com/mamaj/a7b378a5c969e3e32a9e4f9bceb0c5eb.js"></script>

Python: simple class to perform commands and copy files (scp) on ssh using subprocess and native ssh client (OpenSSH).
ssh-utils.py
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}'

@mamaj
Author
mamaj commented on Aug 25, 2022
Feedbacks are welcome!

@mamaj
Author
mamaj commented on Aug 25, 2022
usage:
from ssh_utils import SshClient

client = SshClient(user='username', remote='remote_addr', key_path='/path/to/key.pem')

run a list of commands

client.cmd(['mkdir ~/testdir', 'ls -la', 'echo done!'])

copy files/dirs

client.scp('my_file.txt', '~/testdir')
@linuxguy123
linuxguy123 commented on Jan 1, 2023
Is there a way to pass in the user's password and not use the key ?

@mamaj
Author
mamaj commented on Jan 2, 2023
Not with the native ssh command. Probably need to install sshpass on the remote and modify client.cmd function accordingly.

@linuxguy123
linuxguy123 commented on Jan 3, 2023
Thanks

@larsblumberg
larsblumberg commented on Dec 12, 2024
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.

@istockmarket
Comment

Leave a comment

Footer
© 2025 GitHub, Inc.
Footer navigation
Terms
Privacy
Security
Status
Docs
Contact
Manage cookies
Do not share my personal information

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment