Skip to content

PoweerShell API Reference

TODO: Add a note about WSL and Git Bash on Windows.

API Documentation

hands_scaphoid.objects.PowerShell

Bases: ShellExecutable

A PowerShell-compatible shell executor that translates Unix commands to PowerShell.

This class automatically translates common Unix commands to their PowerShell equivalents when running on Windows systems.

Source code in src/hands_scaphoid/objects/shells/PowerShell.py
class PowerShell(Shell):
    """
    A PowerShell-compatible shell executor that translates Unix commands to PowerShell.

    This class automatically translates common Unix commands to their PowerShell
    equivalents when running on Windows systems.
    """

    # Command translation mapping from Unix to PowerShell
    COMMAND_TRANSLATIONS = {
        "ls": "Get-ChildItem",
        "dir": "Get-ChildItem",
        "cat": "Get-Content",
        "cp": "Copy-Item",
        "copy": "Copy-Item",
        "mv": "Move-Item",
        "move": "Move-Item",
        "rm": "Remove-Item",
        "del": "Remove-Item",
        "mkdir": "New-Item -ItemType Directory",
        "rmdir": "Remove-Item -Recurse",
        "pwd": "Get-Location",
        "cd": "Set-Location",
        "echo": "Write-Output",
        "grep": "Select-String",
        "find": "Get-ChildItem -Recurse | Where-Object",
        "ps": "Get-Process",
        "kill": "Stop-Process",
        "which": "Get-Command",
        "whereis": "Get-Command",
        "curl": "Invoke-WebRequest",
        "wget": "Invoke-WebRequest",
        "head": "Get-Content | Select-Object -First",
        "tail": "Get-Content | Select-Object -Last",
        "wc": "Measure-Object",
        "sort": "Sort-Object",
        "uniq": "Sort-Object -Unique",
        "cut": "Select-Object",
        "awk": "ForEach-Object",
        "sed": "ForEach-Object",
        "env": "Get-ChildItem Env:",
        "export": "Set-Variable",
        "chmod": "Set-ItemProperty",
        "chown": "Set-Acl",
        "df": "Get-WmiObject -Class Win32_LogicalDisk",
        "du": "Get-ChildItem -Recurse | Measure-Object -Property Length -Sum",
        "mount": "Get-WmiObject -Class Win32_LogicalDisk",
        "umount": 'Write-Warning "umount not available in PowerShell"',
        "top": "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10",
        "htop": "Get-Process | Sort-Object CPU -Descending | Select-Object -First 10",
        "free": "Get-WmiObject -Class Win32_OperatingSystem | Select-Object TotalVisibleMemorySize, FreePhysicalMemory",
        "uptime": "Get-WmiObject -Class Win32_OperatingSystem | Select-Object LastBootUpTime",
        "whoami": "[System.Security.Principal.WindowsIdentity]::GetCurrent().Name",
        "id": "[System.Security.Principal.WindowsIdentity]::GetCurrent().Name",
        "hostname": "$env:COMPUTERNAME",
        "uname": "Get-ComputerInfo | Select-Object WindowsProductName, WindowsVersion",
    }

    def __init__(self, *args, **kwargs):
        """Initialize PowerShell with PowerShell as the default shell."""
        super().__init__(*args, **kwargs)
        self.shell_executable = "powershell.exe"
        if platform.system() == "Windows":
            # Try to use PowerShell Core (pwsh) if available, fallback to Windows PowerShell
            try:
                subprocess.run(["pwsh", "-Version"], capture_output=True, check=True)
                self.shell_executable = "pwsh.exe"
            except (subprocess.CalledProcessError, FileNotFoundError):
                self.shell_executable = "powershell.exe"

    def _translate_command(self, command_with_args: str) -> str:
        """
        Translate Unix commands to PowerShell equivalents.

        Args:
            command_with_args: The original command with arguments

        Returns:
            Translated PowerShell command
        """
        if isinstance(command_with_args, str):
            parts = command_with_args.strip().split()
        else:
            parts = command_with_args

        if not parts:
            return command_with_args

        command = parts[0]
        args = parts[1:] if len(parts) > 1 else []

        # Check if we have a translation for this command
        if command in self.COMMAND_TRANSLATIONS:
            translated_cmd = self.COMMAND_TRANSLATIONS[command]

            # Handle special cases for argument translation
            if command == "ls" and args:
                # ls -la -> Get-ChildItem -Force -Name
                ps_args = []
                for arg in args:
                    if arg.startswith("-"):
                        if "a" in arg:
                            ps_args.append("-Force")
                        if "l" in arg:
                            ps_args.append("-Detailed")
                        if "h" in arg:
                            ps_args.append("-Humanized")
                    else:
                        ps_args.append(f'"{arg}"')
                return f"{translated_cmd} {' '.join(ps_args)}"

            elif command == "cp" and len(args) >= 2:
                # cp source dest -> Copy-Item source dest
                source = f'"{args[0]}"' if not args[0].startswith('"') else args[0]
                dest = f'"{args[1]}"' if not args[1].startswith('"') else args[1]
                extra_args = args[2:] if len(args) > 2 else []
                ps_args = [source, dest]
                if "-r" in " ".join(args) or "-R" in " ".join(args):
                    ps_args.append("-Recurse")
                if "-f" in " ".join(args):
                    ps_args.append("-Force")
                ps_args.extend(extra_args)
                return f"{translated_cmd} {' '.join(ps_args)}"

            elif command == "rm" and args:
                # rm -rf file -> Remove-Item file -Recurse -Force
                ps_args = []
                files = []
                for arg in args:
                    if arg.startswith("-"):
                        if "r" in arg or "R" in arg:
                            ps_args.append("-Recurse")
                        if "f" in arg:
                            ps_args.append("-Force")
                    else:
                        files.append(f'"{arg}"' if not arg.startswith('"') else arg)
                return f"{translated_cmd} {' '.join(files)} {' '.join(ps_args)}"

            else:
                # Default: just append arguments
                if args:
                    return f"{translated_cmd} {' '.join(args)}"
                else:
                    return translated_cmd

        # No translation available, return original command
        return command_with_args

    def run(
        self, command_with_args: str, **kwargs: Dict
    ) -> subprocess.CompletedProcess:
        """
        Execute a command, translating Unix commands to PowerShell if needed.

        Args:
            command_with_args: The command to execute
            **kwargs: Additional arguments passed to parent run method

        Returns:
            CompletedProcess object with execution results
        """
        if platform.system() == "Windows":
            # Translate the command for Windows PowerShell
            translated_command = self._translate_command(command_with_args)

            # If the command was translated, we need to run it through PowerShell
            if translated_command != command_with_args:
                # Run the translated command through PowerShell
                ps_command = f'{self.shell_executable} -Command "{translated_command}"'
                return super().run(ps_command, **kwargs)

        # Run the original command
        return super().run(command_with_args, **kwargs)

Functions

__init__(*args, **kwargs)

Initialize PowerShell with PowerShell as the default shell.

Source code in src/hands_scaphoid/objects/shells/PowerShell.py
def __init__(self, *args, **kwargs):
    """Initialize PowerShell with PowerShell as the default shell."""
    super().__init__(*args, **kwargs)
    self.shell_executable = "powershell.exe"
    if platform.system() == "Windows":
        # Try to use PowerShell Core (pwsh) if available, fallback to Windows PowerShell
        try:
            subprocess.run(["pwsh", "-Version"], capture_output=True, check=True)
            self.shell_executable = "pwsh.exe"
        except (subprocess.CalledProcessError, FileNotFoundError):
            self.shell_executable = "powershell.exe"

run(command_with_args, **kwargs)

Execute a command, translating Unix commands to PowerShell if needed.

Parameters:

Name Type Description Default
command_with_args str

The command to execute

required
**kwargs Dict

Additional arguments passed to parent run method

{}

Returns:

Type Description
CompletedProcess

CompletedProcess object with execution results

Source code in src/hands_scaphoid/objects/shells/PowerShell.py
def run(
    self, command_with_args: str, **kwargs: Dict
) -> subprocess.CompletedProcess:
    """
    Execute a command, translating Unix commands to PowerShell if needed.

    Args:
        command_with_args: The command to execute
        **kwargs: Additional arguments passed to parent run method

    Returns:
        CompletedProcess object with execution results
    """
    if platform.system() == "Windows":
        # Translate the command for Windows PowerShell
        translated_command = self._translate_command(command_with_args)

        # If the command was translated, we need to run it through PowerShell
        if translated_command != command_with_args:
            # Run the translated command through PowerShell
            ps_command = f'{self.shell_executable} -Command "{translated_command}"'
            return super().run(ps_command, **kwargs)

    # Run the original command
    return super().run(command_with_args, **kwargs)