Skip to content

Instantly share code, notes, and snippets.

@KernelGhost
Last active June 17, 2026 01:43
Show Gist options
  • Select an option

  • Save KernelGhost/001f29751131e7f00950cffc4e8d5078 to your computer and use it in GitHub Desktop.

Select an option

Save KernelGhost/001f29751131e7f00950cffc4e8d5078 to your computer and use it in GitHub Desktop.
This guide provides step-by-step instructions for setting up and utilising SSH and SCP on an Android device using Termux.

SSH and SCP on Android

Author: Rohan Barar
Date: 03/03/2024

Basic Setup

  1. Install Termux on the Android device using F-Droid.

  2. Update all packages.

    pkg update && pkg upgrade
  3. Grant Termux storage access permissions.

    termux-setup-storage
  4. Install OpenSSH.

    pkg install openssh
  5. Set a password for the current user.

    passwd
  6. Start the OpenSSH server process.

    sshd
  7. Identify the Android device IP address.

    ifconfig
  8. SSH into the Android device from a different machine on the same local network.

    ssh ANDROID_DEVICE_IP_ADDRESS -p 8022
  9. When you are done, stop the OpenSSH server.

    pkill sshd

Copy Examples

  1. Copy a file from the Android device to the Desktop without changing the file name.

    scp -P 8022 -i ~/.ssh/android_device ANDROID_DEVICE_IP_ADDRESS:/storage/emulated/0/a\ folder\ with\ spaces/example\ file.txt ~/Desktop # Key Based Authentication
    scp -P 8022 ANDROID_DEVICE_IP_ADDRESS:/storage/emulated/0/a\ folder\ with\ spaces/example\ file.txt ~/Desktop # Password Based Authentication
  2. Copy a file from a folder on the Desktop to the Android device with a new filename.

    scp -P 8022 -i ~/.ssh/android_device ~/Desktop/Random\ Folder/old_filename.txt ANDROID_DEVICE_IP_ADDRESS:/storage/emulated/0/random\ folder/new_filename.txt # Key Based Authentication
    scp -P 8022 ~/Desktop/Random\ Folder/old_filename.txt ANDROID_DEVICE_IP_ADDRESS:/storage/emulated/0/random\ folder/new_filename.txt # Password Based Authentication
  3. Copy a folder from the Android device to the Desktop.

    scp -P 8022 -i ~/.ssh/android_device -r ANDROID_DEVICE_IP_ADDRESS:/storage/emulated/0/example\ folder\ to\ copy ~/Desktop # Key Based Authentication
    scp -P 8022 -r ANDROID_DEVICE_IP_ADDRESS:/storage/emulated/0/example\ folder\ to\ copy ~/Desktop # Password Based Authentication
  4. Copy a folder from the Desktop to the Android device.

    Note: If the destination folder does not exist, the copied folder will be renamed to the destination folder name.

    scp -P 8022 -i ~/.ssh/android_device -r ~/Desktop/example\ folder ANDROID_DEVICE_IP_ADDRESS:/storage/emulated/0/copy\ of\ example\ folder # Key Based Authentication
    scp -P 8022 -r ~/Desktop/example\ folder ANDROID_DEVICE_IP_ADDRESS:/storage/emulated/0/copy\ of\ example\ folder # Password Based Authentication

Alternatively, GUI-based applications like Cyberduck, FileZilla and WinSCP can be used alongside SFTP.

(Optional) Key-Based Authentication Setup

The following section is based on this excellent YouTube video. For security reasons, it is recommended to enable key-based authentication and disable password-based authentication.

  1. Run ssh-keygen on your local machine (not the Android device) in order to generate a public-private key pair. You can enter a passphrase to secure the private key if you wish. Two files android_device.pub (the public key) and android_device (the private key) will be created in ~/.ssh/.

    ssh-keygen -t ed25519 -f ~/.ssh/android_device -C "My Android Device"
  2. Copy the public key to the Android device using ssh-copy-id.

    ssh-copy-id -p 8022 -i ~/.ssh/android_device.pub ANDROID_DEVICE_IP_ADDRESS
  3. Turn off password-based authentication on the Android device.

    1. Open $PREFIX/etc/ssh/sshd_config for editing.
    2. Locate the line PasswordAuthentication yes and change this to PasswordAuthentication no.
    3. Add the line PubkeyAuthentication yes.
  4. Restart the ssh daemon on the Android device to apply the changes.

    pkill sshd
    sshd
  5. Ensure SSH is working correctly by attempting to connect to the Android device using the newly created key.

    ssh -p 8022 -i ~/.ssh/android_device ANDROID_DEVICE_IP_ADDRESS

(Optional) Symbolic Links

For ease of access, symbolic links to directories, dot files and configuration files can be created. Below is an example demonstrating the creation of a symbolic link to the root directory of the device's internal storage.

ln -s /storage/emulated/0 ~/symlinks/droot

You can add symbolic links to directories to CDPATH, which is a colon-separated shell environment variable that stores a list of directories to be used as search paths for the built-in cd command. Whilst the symbolic link only needs to be created once, modifications to CDPATH need to be placed in ~/.bash_profile such that CDPATH is correctly set each time a new terminal session is created.

export CDPATH="~/symlinks:$CDPATH"

Setting CDPATH causes auto-echoing of directory paths following use of the cd command. To disable this, add the following function to your ~/.bash_profile.

# Change Directory (cd)
# Overrides the builtin 'cd' command to prevent auto-echoing of directory paths whilst CDPATH is set.
function cd() {
    local destination=""
    if [ -z "$*" ]; then 
        destination=~
    else
        destination=$*
    fi

    builtin cd "${destination}" >/dev/null
}

(Optional) Automatic Initialisation of sshd on Terminal Startup

You can configure sshd to run automatically each time a new Termux session is commenced. Add the following code to a new file located at ~/.scripts/ip_and_sshd_funcs.sh.

# Device IP and SSHD Functions
# Global Variables
ETHERNET_IP_ADDR=""
WIRELESS_LAN_IP_ADDR=""
HOTSPOT_IP_ADDR=""
PUBLIC_IP_ADDR=""

# Device IP Address Functions
# Display Local Device IP Addresses
function echo_local_ip() {
    # Note: Cannot use run 'ifconfig wlan0' directly due to error 'Warning: cannot open /proc/net/dev (Permission denied)'
    # 'wlan0' - WIFI
    WIRELESS_LAN_IP_ADDR="$(ifconfig 2>/dev/null | grep -A1 '^wlan0' | grep -Eo 'inet ([0-9]{1,3}\.){3}[0-9]{1,3}' | cut -d' ' -f2)"
    # 'eth0' - Ethernet
    ETHERNET_IP_ADDR="$(ifconfig 2>/dev/null | grep -A1 '^eth0' | grep -Eo 'inet ([0-9]{1,3}\.){3}[0-9]{1,3}' | cut -d' ' -f2)"
    # 'swlan0' - Hotspot
    HOTSPOT_IP_ADDR="$(ifconfig 2>/dev/null | grep -A1 '^swlan0' | grep -Eo 'inet ([0-9]{1,3}\.){3}[0-9]{1,3}' | cut -d' ' -f2)"

    # 'wlan0'
    if [ -n "$WIRELESS_LAN_IP_ADDR" ]; then
        echo -e "\e[38;5;003mWLAN IP Address:\e[0m ${WIRELESS_LAN_IP_ADDR}"
    else
        echo -e "\e[38;5;003mWLAN IP Address:\e[0m None"
    fi

    # 'eth0'
    if [ -n "$ETHERNET_IP_ADDR" ]; then
        echo -e "\e[38;5;003mLAN IP Address:\e[0m ${ETHERNET_IP_ADDR}"
    fi

    # 'swlan0'
    if [ -n "$HOTSPOT_IP_ADDR" ]; then
        echo -e "\e[38;5;003mHotspot IP Address:\e[0m ${HOTSPOT_IP_ADDR}"
    fi
}

# Display Public Device IP Address
function echo_public_ip() {
    local target_website="ifconfig.co"
    PUBLIC_IP_ADDR="$(curl -s -4 --connect-timeout 3 $target_website)"

    if [ $? -eq 0 ]; then
        echo -e "\e[38;5;003mPublic IP Address:\e[0m ${PUBLIC_IP_ADDR}"
    else
        echo -e "\e[38;5;003mPublic IP Address:\e[0m None"
    fi
}

# Start 'OpenSSH' Server
function startsshd() {
    # OpenSSH Port
    local ssh_port="8022"

    # Is OpenSSH installed?
    local is_openssh_installed=$(command -v ssh &> /dev/null && echo true || echo false)
    # Is the device connected to a local network?
    local is_local_network=$( [[ -n $WIRELESS_LAN_IP_ADDR || -n $ETHERNET_IP_ADDR || -n $HOTSPOT_IP_ADDR ]] && echo "true" || echo "false" )
    # Is the OpenSSH server already running?
    local is_server_running=$(pgrep sshd >/dev/null && echo "true" || echo "false")

    if [ "$is_openssh_installed" == true ] && [ "$is_local_network" = true ] && [ "$is_server_running" = false ]; then
        sshd
        echo -e "\e[38;5;002mOpenSSH Server:\e[0m Started on Port ${ssh_port}."
    elif [ "is_openssh_installed" == true ] && [ "$is_local_network" = true ] && [ "$is_server_running" = true ]; then
        echo -e "\e[38;5;002mOpenSSH Server:\e[0m Running on Port ${ssh_port}."
    fi
}

Then, add the following to your ~/.bash_profile.

# Source 'ip_and_sshd_funcs.sh'
source ~/.scripts/ip_and_sshd_funcs.sh

# Display Local IP Addresses
echo_local_ip

# Start OpenSSH Server
startsshd

Bash Scripts (GNU/Linux & macOS)

To simplify the process of connecting to the device:

  1. Include the following code snippet within ~/.scripts/android.sh.
  2. Include source ~/.scripts/android.sh within your ~/.bash_profile or ~/.bashrc.
  3. Ensure your Android device MAC address (e.g. "a0:b1:c2:d3:e4:f5") is stored at ~/.scripts/android_mac_address.txt.

Note that Android MAC addresses are randomised by default as a security measure. This feature must be disabled to utilise the automatic MAC address-based device detection capabilities of the code snippet. On Samsung devices, this can be achieved for the current Wi-Fi network by navigating to Settings --> Connections --> Wi-Fi --> Gear Icon --> View more --> MAC address type and changing "Randomised MAC" to "Phone MAC".

#!/usr/bin/env bash
# shellcheck disable=SC2155

# VARIABLES
WLANSCAN_FORCE=1

# CONSTANTS
readonly ANDROID_MAC_ADDRESS="$(cat ~/.scripts/android_mac_address.txt)"
readonly ANDROID_PORT="8022"
readonly ANDROID_ROOT="/storage/emulated/0"
readonly KEY_AUTH=0
readonly OS=$(uname)
readonly ANSI_BOLD="\033[1m"
readonly ANSI_BOLD_BLUE="\033[1;34m"
readonly ANSI_BOLD_CYAN="\033[01;38;5;006m"
readonly ANSI_BOLD_GREEN="\033[1;32m"
readonly ANSI_BOLD_RED="\033[1;31m"
readonly ANSI_BOLD_YELLOW="\033[1;33m"
readonly ANSI_CYAN="\033[00;38;5;006m"
readonly ANSI_RESET="\033[0m"

# FUNCTIONS
# Name: chkos
# Purpose: Checks whether this script is being sourced on a supported OS.
# Usage: chkos
# Output:
#    - Returns exit status '0' if being run on a supported OS.
#    - Returns exit status '1' if not being run on a supported OS.
function chkos() {
    if [[ "$OS" != "Darwin" && "$OS" != "Linux" ]]; then
        echo -e "${ANSI_BOLD_RED}ERROR${ANSI_RESET}"
        echo "Unsupported operating system!"
        return 1
    fi
}

# Name: chkdeps
# Purpose: Checks whether dependencies are installed.
# Usage: chkdeps
# Output:
#    - Returns exit status '0' if all dependencies are installed.
#    - Returns exit status '1' if not all dependencies are installed.
function chkdeps() {
    # Initialise missing dependency counter.
    local missing_dependencies=0
    
    # Initialise list of dependencies.
    declare -A dependency_list=(
        ["net-tools"]="arp"
        ["gawk"]="awk"
        ["nmap"]="nmap"
    )
    
    # Add additional OS-specific dependencies.
    if [ "$OS" = "Linux" ]; then
        dependency_list["iproute2"]="ip"
        dependency_list["jq"]="jq"
    fi

    # Check whether dependencies are installed.
    for dependency_name in "${!dependency_list[@]}"; do
        if ! command -v "${dependency_list[$dependency_name]}" &>/dev/null; then
            if [ "$missing_dependencies" -eq 0 ]; then
                echo -e "${ANSI_BOLD_RED}ERROR${ANSI_RESET}"
            fi
            
            echo "Dependency '${dependency_name}' not installed!"
            missing_dependencies=$((missing_dependencies+1))
        fi
    done

    # Return a non-zero exit code if any dependencies were missing.
    if [ "$missing_dependencies" -gt 0 ]; then
        return 1
    fi
}

# Name: getipaddrs
# Purpose: Return a list of IP addresses and their corresponding CIDRs.
# Usage: getipaddrs
# Output: Array containing IP addresses with their corresponding CIDRs.
function getipaddrs() {
    # Declare output array using a nameref.
    local -n return_arr=$1

    if [ "$OS" = "Linux" ]; then
        # Store LAN/WLAN IPv4 addresses in JSON array.
        local ip_addr_arr_json=$(ip -j addr | jq -r '[.[] | select(.link_type == "ether" or .link_type == "wlan" or (.ifname | startswith("wl"))) | .addr_info[] | select(.family == "inet" and .local != "127.0.0.1") | "\(.local)/\(.prefixlen)"]')

        # Convert JSON array to Bash array.
        # shellcheck disable=SC2034
        readarray -t return_arr <<< "$(echo "$ip_addr_arr_json" | jq -r '.[]')"
    elif [ "$OS" = "Darwin" ]; then
        local ip_addr=$(networksetup -getinfo "Wi-Fi" | grep "IP address" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}')
        local subnet_mask=$(networksetup -getinfo "Wi-Fi" | grep "Subnet mask" | grep -oE '([0-9]{1,3}\.){3}[0-9]{1,3}')
        local cidr=$(echo "$subnet_mask" | awk -F'.' '{
            for (i = 1; i <= 4; i++) {
                while ($i > 0) {
                    total += $i % 2;
                    $i = int($i / 2);
                }
            }
            print total
        }')

        # shellcheck disable=SC2034
        return_arr=("${ip_addr}/${cidr}")
    fi
}

# Name: wlanscan
# Purpose: Display the IP and MAC addresses of devices on the same WLAN.
# Usage: wlanscan [--force] [return_string]
# Output: Prints a table of device IP and MAC addresses to stdout.
function wlanscan() {
    # Declare variables.
    local ip_addr_arr=()
    local awk_script=""
    local force_rescan=0
    local export_output=0

    # Process arguments.
    for argument in "$@"; do
        if [ "$argument" = "--force" ]; then
            force_rescan=1
        else
            # Declare output string using a nameref.
            local -n output_table=$argument
            export_output=1
        fi
    done
    
    if [ "$export_output" -eq 0 ]; then
        # Declare output variable as an ordinary string.
        local output_table=""
    fi

    # Check operating system and dependencies.
    if chkos && chkdeps; then
        # Request IP address list.
        getipaddrs ip_addr_arr

        # Check if the computer is connected to a WLAN.
        if [ ${#ip_addr_arr[@]} -gt 0 ]; then
            # Run 'nmap' if 'WLANSCAN_FORCE' is '1' or '--force' is specified.
            if [ "$WLANSCAN_FORCE" -eq 1 ] || [ "$force_rescan" -eq 1 ]; then
                # Alert user.
                echo -e "${ANSI_BOLD_BLUE}INFO:${ANSI_RESET} Discovering devices on the WLAN. Please wait...\n"

                # Set 'WLANSCAN_FORCE' to '0'.
                WLANSCAN_FORCE=0

                # Loop over each wireless network interface.
                for ip_addr in "${ip_addr_arr[@]}"; do
                    # Store IP and CIDR separately.
                    local ip=$(echo "$ip_addr" | awk -F/ '{print $1}')
                    local cidr=$(echo "$ip_addr" | awk -F/ '{print $2}')

                    # Convert CIDR to dotted-decimal netmask.
                    # - The CIDR is a method of specifying the subnet mask.
                    # - The CIDR is calculated by counting the number of bits that belong to the routing prefix within an IPv4 address.
                    # - The acceptable range for CIDR values associated with IPv4 addresses is 0 to 32 inclusive.
                    # - The subnet mask is guaranteed to be a 32-bit binary number with consecutive ones followed by any remaining zeroes.
                    # - The CIDR is determined by identifying the number of set bits within the subnet mask.
                    # - E.g. '32' implies a subnet mask of 255.255.255.255 (0xffffffff), with a usable host range of 'W.X.Y.Z' (0 bits).
                    # - E.g. '24' implies a subnet mask of 255.255.255.0 (0xffffff00), with a usable host range of 'W.X.Y.1' to 'W.X.Y.254' (8 bits).
                    # - E.g. '20' implies a subnet mask of 255.255.240.0 (0xfffff000), with a usable host range of 'W.X.Y.1' to 'W.X.Y+15.254' (12 bits).
                    # - E.g. '16' implies a subnet mask of 255.255.0.0 (0xffff0000), with a usable host range of 'W.X.0.1' to 'X.X.255.254' (16 bits).
                    # - E.g. '0' implies a subnet mask of 0.0.0.0 (0x00000000), with a usable host range of '0.0.0.1' to '255.255.255.254' (32 bits).
                    local bits=$(( 32 - cidr ))
                    local netmask_dec=$(( (0xFFFFFFFF << bits) & 0xFFFFFFFF ))
                    #local netmask=$(printf "%d.%d.%d.%d" $(( (netmask_dec >> 24) & 0xFF )) $(( (netmask_dec >> 16) & 0xFF )) $(( (netmask_dec >> 8) & 0xFF )) $(( netmask_dec & 0xFF )))

                    # Convert IP address to decimal.
                    local ip_dec=$(echo "$ip" | tr "." " " | awk '{print $1*2^24 + $2*2^16 + $3*2^8 + $4}')

                    # Determine the routing prefix.
                    local rp_dec=$(( netmask_dec & ip_dec ))
                    local routing_prefix=$(printf "%d.%d.%d.%d" $(( (rp_dec >> 24) & 0xFF )) $(( (rp_dec >> 16) & 0xFF )) $(( (rp_dec >> 8) & 0xFF )) $(( rp_dec & 0xFF )))

                    # Construct the 'nmap' command.
                    # Note: This will scan IP addresses without DNS resolution or port scanning.
                    local nmap_command=(nmap "${routing_prefix}"/"${cidr}" -sn -n)

                    # Run 'nmap'.
                    "${nmap_command[@]}" &>/dev/null
                done
            else
                # Alert user.
                echo -e "${ANSI_BOLD_YELLOW}WARNING!${ANSI_RESET} Using cached Address Resolution Protocol (ARP) entries. Run 'wlanscan --force' to rebuild the cache.\n"
            fi

            # Run 'arp'.
            # Note: 'arp' only lists devices the system recently communicated with.
            # Note: 'arp -n -a' returns results in the format:
            # ? (10.168.0.1) at 2c:c8:1b:1:c6:83 on en0 ifscope [ethernet]
            # ? (192.168.0.34) at (incomplete) on en0 ifscope [ethernet]
            # ? (224.0.0.251) at 1:0:5e:0:0:fb on en0 ifscope permanent [ethernet]
            # ? (239.255.255.250) at 1:0:5e:7f:ff:fa on en0 ifscope permanent [ethernet]
            # Hence, the IP address is at '$2' and the MAC address is at '$4'.
            # IP addresses at '$2' need to be reformatted to remove open and close parentheses.
            # MAC addresses at '$4' need to be reformatted to ensure all 12 digits are displayed.

            # Store awk script within a variable.
            # shellcheck disable=SC2016
            awk_script='
            BEGIN {
                # Initialise row counter.
                row_counter = 0

                # Print table header.
                printf "%s%-8s%-20s%-15s%s\n", bold, "Device", "MAC Address", "IP Address", reset
            }
            {
                # Remove occurrences of open or close parentheses.
                gsub(/[\(\)]/, "")

                # Only print the table row if valid MAC and IP addresses are present for the device.
                if ($4 ~ /([0-9a-fA-F]{1,2}:){5}[0-9a-fA-F]{1,2}/ && $2 ~ /([0-9]{1,3}\.){3}[0-9]{1,3}/) {
                    # Increment row counter.
                    row_counter++

                    # Split the MAC address into individual fields.
                    split($4, mac_parts, ":")

                    # Combine each formatted field to create a MAC address with exactly 12 hexadecimal digits.
                    mac_address = ""
                    for (i = 1; i <= 6; i++)
                        mac_address = mac_address sprintf(":%02s", mac_parts[i])
                    mac_address = substr(mac_address, 2, length(mac_address) - 1)

                    # Print the table row.
                    printf "%-8s%-20s%-15s\n", sprintf("%06s", row_counter), mac_address, $2
                }
            }
            END {
                OFS=""
                printf "\n%sCOMPLETE!%s %d devices found.\n", green, reset, row_counter
            }'

            # Capture the output into a variable.
            output_table=$(arp -n -a | awk -v bold="$ANSI_BOLD" -v green="$ANSI_BOLD_GREEN" -v reset="$ANSI_RESET" "$awk_script")

            # Print the output if an additional argument was not supplied.
            if [ "$export_output" -eq 0 ]; then
                echo "$output_table"
            fi
        else
            # Alert user.
            echo -e "${ANSI_BOLD_RED}ERROR!${ANSI_RESET} The computer is not connected to a WLAN."
            return 1
        fi
    fi
}

# Name: sshdroid
# Purpose: SSH into an Android device given a MAC or IP address.
# Usage: sshdroid [mac_address | ip_address]
# - Example: sshdroid
# - Example: sshdroid 32:34:e5:c1:a3:ba
# - Example: sshdroid 192.168.0.16
function sshdroid() {
    # Declare variables.
    local ip_addr_arr=()
    local mac_address=""
    local ip_address=""
    local wlanscan_table=""
    
    # Check operating system and dependencies.
    if chkos && chkdeps; then
        # Request IP address list.
        getipaddrs ip_addr_arr

        # Check if the computer is connected to a WLAN.
        if [ ${#ip_addr_arr[@]} -gt 0 ]; then
            # If no additional parameter is provided, use the default device MAC address.
            if [ -z "$1" ]; then
                echo -e "${ANSI_BOLD_BLUE}INFO:${ANSI_RESET} Using default device MAC address ${ANDROID_MAC_ADDRESS}..."
                mac_address="$ANDROID_MAC_ADDRESS"
            # If an IP address is provided, use it.
            elif [[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
                echo -e "${ANSI_BOLD_BLUE}INFO:${ANSI_RESET} Using local IP address ${1}..."
                ip_address="$1"
            # If a MAC address is provided, use it instead of the default.
            elif [[ "$1" =~ ^([0-9a-fA-F]{1,2}:){5}[0-9a-fA-F]{1,2}$ ]]; then
                echo -e "${ANSI_BOLD_BLUE}INFO:${ANSI_RESET} Using device MAC address ${1}..."
                mac_address="$1"
            else
            # Ignore invalid argument
                echo -e "${ANSI_BOLD_YELLOW}WARNING!${ANSI_RESET} Invalid argument '${1}'. Ignoring."
                echo -e "${ANSI_BOLD_BLUE}INFO:${ANSI_RESET} Using default device MAC address ${ANDROID_MAC_ADDRESS}..."
                mac_address="$ANDROID_MAC_ADDRESS"
            fi

            if [ -z "$ip_address" ]; then
                # If a device IP address was not explicitly provided, use the device MAC address to find it.
                wlanscan wlanscan_table
                ip_address=$(echo "$wlanscan_table" | grep "$mac_address" | grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}")

                if [ -z "$ip_address" ]; then
                    # Alert user.
                    echo -e "${ANSI_BOLD_RED}ERROR!${ANSI_RESET} The device is not present on the network."
                    return 1
                fi
            else
                # If a device IP address was explicitly provided, ping the device to check it exists on the network.
                if ! ping -c 1 -W 3 "$ip_address" &>/dev/null; then
                    # Alert user.
                    echo -e "${ANSI_BOLD_RED}ERROR!${ANSI_RESET} The device is not present on the network."
                    return 1
                fi
            fi

            # Assuming no errors were triggered above, the device was found.
            echo -e "${ANSI_BOLD_GREEN}SUCCESS!${ANSI_RESET} Device found at ${ip_address}!"

            # Run SSH.
            if [ "$KEY_AUTH" -eq 0 ]; then
                ssh -o ConnectTimeout=5 "$ip_address" -p "$ANDROID_PORT"
            else
                ssh -o ConnectTimeout=5 "$ip_address" -p "$ANDROID_PORT" -i ~/.ssh/android_device
            fi
        else
            # Alert user.
            echo -e "${ANSI_BOLD_RED}ERROR!${ANSI_RESET} The computer is not connected to a WLAN."
            return 1
        fi
    fi
}

# Name: scpdroid
# Purpose: Run SCP given a device MAC or IP address.
# Usage: scpdroid [mac_address | ip_address] (todev | fromdev) [flags] source_path target_path
# - Example: scpdroid 32:34:e5:c1:a3:ba todev -r "~/Desktop/example folder" .
# - Example: scpdroid fromdev "Random folder/example file.txt" ~/Desktop
function scpdroid() {
    # Declare variables.
    local ip_addr_arr=()
    local mac_address=""
    local ip_address=""
    local tx_direction=""
    local flags=()
    local source_path=""
    local destination_path=""
    local scp_command=()
    local wlanscan_table=""

    # Check operating system and dependencies.
    if chkos && chkdeps; then
        # Request IP address list.
        getipaddrs ip_addr_arr

        # Check if the computer is connected to a WLAN.
        if [ ${#ip_addr_arr[@]} -gt 0 ]; then
            # Determine which MAC or IP address to use.
            if [[ "$1" == "todev" || "$1" == "fromdev" ]]; then
                # If no additional parameter is provided, use the default device MAC address.
                echo -e "${ANSI_BOLD_BLUE}INFO:${ANSI_RESET} Using default device MAC address ${ANDROID_MAC_ADDRESS}..."
                mac_address="$ANDROID_MAC_ADDRESS"
            elif echo "$1" | grep -qE '([0-9]{1,3}\.){3}[0-9]{1,3}'; then
                # If an IP address is provided, use it.
                echo -e "${ANSI_BOLD_BLUE}INFO:${ANSI_RESET} Using local IP address ${1}..."
                ip_address="$1"
                shift # Shift to the next argument
            elif echo "$1" | grep -qE '([0-9a-fA-F]{1,2}:){5}[0-9a-fA-F]{1,2}'; then
                # If a MAC address is provided, use it instead of the default.
                echo -e "${ANSI_BOLD_BLUE}INFO:${ANSI_RESET} Using device MAC address ${1}..."
                mac_address="$1"
                shift # Shift to the next argument
            else
                # Throw an error.
                echo -e "${ANSI_BOLD_RED}ERROR!${ANSI_RESET} Invalid command. Please follow the command format 'scpdroid [mac_address | ip_address] (todev | fromdev) [flags] source_path target_path'."
                return 1
            fi

            if [ -z "$ip_address" ]; then
                # If a device IP address was not explicitly provided, use the device MAC address to find it.
                wlanscan wlanscan_table
                ip_address=$(echo "$wlanscan_table" | grep "$mac_address" | grep -Eo "([0-9]{1,3}\.){3}[0-9]{1,3}")

                if [ -z "$ip_address" ]; then
                    # Alert user.
                    echo -e "${ANSI_BOLD_RED}ERROR!${ANSI_RESET} The device is not present on the network."
                    return 1
                fi
            else
                # If a device IP address was explicitly provided, ping the device to check it exists on the network.
                if ! ping -c 1 -W 3 "$ip_address" >/dev/null; then
                    # Alert user.
                    echo -e "${ANSI_BOLD_RED}ERROR!${ANSI_RESET} The device is not present on the network."
                    return 1
                fi
            fi

            # Assuming no errors were triggered above, the device was found.
            echo -e "${ANSI_BOLD_GREEN}SUCCESS!${ANSI_RESET} Device found at ${ip_address}!"

            # Identify the file/folder transfer direction.
            # This is expected to be either 'todev' or 'fromdev'.
            tx_direction="$1"
            shift # Shift to the next argument
            if [[ "$tx_direction" == "todev" || "$tx_direction" == "fromdev" ]]; then
                # Parse any additional flags (e.g. '-r').
                while [[ "$#" -gt 0 && "$1" == -* ]]; do
                    flags+=("$1")
                    shift # Shift to the next argument
                done

                # Check if 2 arguments remain (a source and target path).
                if [ "$#" -eq 2 ]; then
                    # Source (Second Last Argument).
                    # shellcheck disable=SC2001
                    source_path="${1//\\}"
                    shift # Shift to the next argument
                    
                    # Destination (Last Argument).
                    # shellcheck disable=SC2001
                    destination_path="${1//\\}"
                    shift # Shift to the next argument

                    # Check if files/folders are being copied to the device ('todev') or from the device ('fromdev').
                    if [ "$tx_direction" = "todev" ]; then
                        # Append Android device root path to destination path.
                        destination_path="${ANDROID_ROOT}/${destination_path}"

                        # Construct the final SCP command as an array.
                        if [ "$KEY_AUTH" -eq 0 ]; then
                            scp_command=("scp" "-o" "ConnectTimeout=5" "-P" "$ANDROID_PORT" "${flags[@]}" "\"${source_path}\"" "${ip_address}:\"${destination_path}\"")
                        else
                            scp_command=("scp" "-o" "ConnectTimeout=5" "-P" "$ANDROID_PORT" "-i" "{HOME}/.ssh/android_device" "${flags[@]}" "\"${source_path}\"" "${ip_address}:\"${destination_path}\"")
                        fi

                        # Print Parameters
                        echo -e "\n${ANSI_BOLD_CYAN}SCP Parameters${ANSI_RESET}"
                        printf "${ANSI_CYAN}%-16s %s${ANSI_RESET}\n" "Direction:" "To Device"
                        printf "${ANSI_CYAN}%-16s %s${ANSI_RESET}\n" "Port:" "$ANDROID_PORT"
                        printf "${ANSI_CYAN}%-16s %s${ANSI_RESET}\n" "Source:" "\"${source_path}\""
                        printf "${ANSI_CYAN}%-16s %s${ANSI_RESET}\n" "Destination:" "${ip_address}:\"${destination_path}\""
                        printf "${ANSI_CYAN}%-16s %s${ANSI_RESET}\n" "Flags:" "${flags[@]}"
                        if [ "$KEY_AUTH" -ne 0 ]; then
                            printf "${ANSI_CYAN}%-16s %s${ANSI_RESET}\n" "Key:" "{HOME}/.ssh/android_device"
                        fi
                        echo -e "\n${ANSI_BOLD_CYAN}SCP Command${ANSI_RESET}"
                        echo -e "${ANSI_CYAN}${scp_command[*]}${ANSI_RESET}\n"

                        # Run SCP
                        # shellcheck disable=SC2294
                        eval "${scp_command[@]}"
                    elif [ "$tx_direction" = "fromdev" ]; then
                        # Append Android device root path to source path.
                        source_path="${ANDROID_ROOT}/${source_path}"

                        # Construct the final SCP command as an array.
                        if [ "$KEY_AUTH" -eq 0 ]; then
                            scp_command=("scp" "-o" "ConnectTimeout=5" "-P" "$ANDROID_PORT" "${flags[@]}" "${ip_address}:\"${source_path}\"" "\"${destination_path}\"")
                        else
                            scp_command=("scp" "-o" "ConnectTimeout=5" "-P" "$ANDROID_PORT" "-i" "${HOME}/.ssh/android_device" "${flags[@]}" "${ip_address}:\"${source_path}\"" "\"${destination_path}\"")
                        fi

                        # Print Parameters
                        echo -e "\n${ANSI_BOLD_CYAN}SCP Parameters${ANSI_RESET}"
                        printf "${ANSI_CYAN}%-16s %s${ANSI_RESET}\n" "Direction:" "From Device"
                        printf "${ANSI_CYAN}%-16s %s${ANSI_RESET}\n" "Port:" "$ANDROID_PORT"
                        printf "${ANSI_CYAN}%-16s %s${ANSI_RESET}\n" "Source:" "${ip_address}:\"${source_path}\""
                        printf "${ANSI_CYAN}%-16s %s${ANSI_RESET}\n" "Destination:" "\"${destination_path}\""
                        printf "${ANSI_CYAN}%-16s %s${ANSI_RESET}\n" "Flags:" "${flags[@]}"
                        if [ "$KEY_AUTH" -ne 0 ]; then
                            printf "${ANSI_CYAN}%-16s %s${ANSI_RESET}\n" "Key:" "{HOME}/.ssh/android_device"
                        fi
                        echo -e "\n${ANSI_BOLD_CYAN}SCP Command${ANSI_RESET}"
                        echo -e "${ANSI_CYAN}${scp_command[*]}${ANSI_RESET}\n"

                        # Run SCP
                        # shellcheck disable=SC2294
                        eval "${scp_command[@]}"
                    fi
                else
                    # Alert user.
                    echo -e "${ANSI_BOLD_RED}ERROR!${ANSI_RESET} An incorrect number of paths were supplied. Please provide a source path followed by a target path."
                    return 1
                fi
            else
                # Alert user.
                echo -e "${ANSI_BOLD_RED}ERROR!${ANSI_RESET} An invalid file/folder transfer direction was specified. Please specify either \"todev\" or \"fromdev\""
                return 1
            fi
        else
            # Alert user.
            echo -e "${ANSI_BOLD_RED}ERROR!${ANSI_RESET} The computer is not connected to a WLAN."
            return 1
        fi
    fi
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment