commit 7e27284ae5e454afdfe484a1da5c491c5214fcf9 from: mtmn date: Wed Jul 1 22:45:58 2026 UTC diggah: add port to ssh connection commit - bccd2a000cccad112dd2bf9d43fb36702492ede1 commit + 7e27284ae5e454afdfe484a1da5c491c5214fcf9 blob - bb88cc5c416c2527558dedae96edea1b6d909576 blob + 70127e81d0c09e320a6bb8292983ecad644caf90 --- diggah/diggah.py +++ diggah/diggah.py @@ -98,6 +98,7 @@ class RemoteExecutor: start_dt: datetime | None, end_dt: datetime | None, include_files: bool, + port: int | None = None, ) -> Iterable[str]: """ Executes a find command on the remote host and yields the results. @@ -107,6 +108,14 @@ class RemoteExecutor: if "@" in host_str: username, hostname = host_str.split("@", 1) + if ":" in hostname: + host_part, _, port_part = hostname.rpartition(":") + if not port_part.isdigit(): + raise ValueError(f"invalid port '{port_part}' in host '{host_str}'") + hostname = host_part + if port is None: + port = int(port_part) + # Load SSH config ssh_config = paramiko.SSHConfig() user_config_file = os.path.expanduser("~/.ssh/config") @@ -122,7 +131,8 @@ class RemoteExecutor: if username is None and "user" in user_config: username = user_config["user"] - port = int(user_config.get("port", 22)) + if port is None: + port = int(user_config.get("port", 22)) key_filename = user_config.get("identityfile") cmd_parts = ["find", path] @@ -198,6 +208,7 @@ class Diggah: path: str = ".", dry_run: bool = False, host: str | None = None, + port: int | None = None, relative: bool = False, output: str | bool = False, ): @@ -215,7 +226,10 @@ class Diggah: all: If True, show all finds regardless of time. path: Directory to search in (default: "."). dry_run: If True, print calculated dates and exit. - host: SSH host to run the command on (e.g., user@example.com). + host: SSH host to run the command on (e.g., user@example.com + or user@example.com:2222). + port: SSH port for the remote host (overrides host:port and + ssh config; default 22). relative: If True, output paths relative to the search root. output: If True or a path, write output to file(s) instead of stdout. If a path is provided, writes to that file. @@ -234,10 +248,12 @@ class Diggah: for start_dt, end_dt, outfile in ranges: if dry_run: self._print_dry_run( - start_dt, end_dt, path, files, host, relative, output, outfile + start_dt, end_dt, path, files, host, port, relative, output, outfile ) else: - self._run_search(start_dt, end_dt, path, files, host, relative, outfile) + self._run_search( + start_dt, end_dt, path, files, host, port, relative, outfile + ) def _determine_search_ranges( self, @@ -332,6 +348,7 @@ class Diggah: path: str, include_files: bool, host: str | None, + port: int | None, relative: bool, output: str | bool, outfile: str | None, @@ -342,6 +359,8 @@ class Diggah: print(f"Include Files: {include_files}") if host: print(f"Host: {host}") + if port: + print(f"Port: {port}") print("Mode: Remote (find command)") else: print("Mode: Local (python walk)") @@ -357,13 +376,14 @@ class Diggah: path: str, include_files: bool, host: str | None, + port: int | None, relative: bool, outfile: str | None, ): results: Iterable[str] if host: results = RemoteExecutor().execute( - host, path, start_dt, end_dt, include_files + host, path, start_dt, end_dt, include_files, port ) else: start_ts = start_dt.timestamp() if start_dt else None