Last active
July 14, 2026 04:46
-
-
Save Yunuuuu/9cc8de7705c5e8fd9cb5909440705260 to your computer and use it in GitHub Desktop.
Install or update FRP from the SourceForge mirror.
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
| # Install or update FRP from the SourceForge mirror. | |
| # | |
| # Usage: | |
| # _install_frp [OPTIONS] | |
| # _install_frp -h | |
| # unset -f _install_frp | |
| # | |
| # Default mode: --user | |
| # | |
| # Default user layout: | |
| # ${XDG_DATA_HOME:-$HOME/.local/share}/frp | |
| # ${XDG_CONFIG_HOME:-$HOME/.config}/frp/frpc.toml | |
| # ${XDG_CONFIG_HOME:-$HOME/.config}/frp/frps.toml | |
| # $HOME/.local/bin/frpc | |
| # $HOME/.local/bin/frps | |
| # | |
| # Default system layout: | |
| # /usr/local/share/frp | |
| # /usr/local/etc/frp/frpc.toml | |
| # /usr/local/etc/frp/frps.toml | |
| # /usr/local/bin/frpc | |
| # /usr/local/bin/frps | |
| # | |
| # Uninstall the default user installation: | |
| # systemctl --user disable --now frpc 2>/dev/null || true | |
| # systemctl --user disable --now frps 2>/dev/null || true | |
| # rm -f -- "$HOME/.local/bin/frpc" "$HOME/.local/bin/frps" | |
| # rm -rf -- "${XDG_DATA_HOME:-$HOME/.local/share}/frp" | |
| # | |
| # Also remove the user configuration: | |
| # rm -rf -- "${XDG_CONFIG_HOME:-$HOME/.config}/frp" | |
| # | |
| # Uninstall the default system installation: | |
| # sudo systemctl disable --now frpc 2>/dev/null || true | |
| # sudo systemctl disable --now frps 2>/dev/null || true | |
| # sudo rm -f -- /usr/local/bin/frpc /usr/local/bin/frps | |
| # sudo rm -rf -- /usr/local/share/frp | |
| # | |
| # Also remove the system configuration: | |
| # sudo rm -rf -- /usr/local/etc/frp | |
| _install_frp() { | |
| local scope="user" | |
| local scope_set=0 | |
| local data_home="" | |
| local data_home_set=0 | |
| local bin_dir="" | |
| local bin_dir_set=0 | |
| local config_home="" | |
| local frp_home="" | |
| local binary_mode="" | |
| local data_dir_mode="" | |
| local config_dir_mode="" | |
| local bin_dir_mode="" | |
| local machine="" | |
| local archive_arch="" | |
| local tmp="" | |
| local source_dir="" | |
| local latest_dir="" | |
| local download_path="" | |
| local new_frpc="" | |
| local new_frps="" | |
| local file="" | |
| local name="" | |
| local rc=1 | |
| local base="https://sourceforge.net/projects/frp.mirror/files" | |
| while (($# > 0)); do | |
| case "$1" in | |
| --user) | |
| if ((scope_set)) && [[ $scope != user ]]; then | |
| echo "Cannot use --user and --system together" >&2 | |
| return 2 | |
| fi | |
| scope="user" | |
| scope_set=1 | |
| shift | |
| ;; | |
| --system) | |
| if ((scope_set)) && [[ $scope != system ]]; then | |
| echo "Cannot use --user and --system together" >&2 | |
| return 2 | |
| fi | |
| scope="system" | |
| scope_set=1 | |
| shift | |
| ;; | |
| --data-home) | |
| if (($# < 2)); then | |
| echo "--data-home requires a value" >&2 | |
| return 2 | |
| fi | |
| data_home="$2" | |
| data_home_set=1 | |
| shift 2 | |
| ;; | |
| --data-home=*) | |
| data_home="${1#*=}" | |
| data_home_set=1 | |
| shift | |
| ;; | |
| --bin-dir) | |
| if (($# < 2)); then | |
| echo "--bin-dir requires a value" >&2 | |
| return 2 | |
| fi | |
| bin_dir="$2" | |
| bin_dir_set=1 | |
| shift 2 | |
| ;; | |
| --bin-dir=*) | |
| bin_dir="${1#*=}" | |
| bin_dir_set=1 | |
| shift | |
| ;; | |
| -h | --help) | |
| cat <<'HELP' | |
| Usage: | |
| _install_frp [OPTIONS] | |
| Options may appear in any order. | |
| Options: | |
| --user | |
| Install for the current user. This is the default mode. | |
| --system | |
| Install system-wide. Root privileges are required. | |
| --data-home PATH | |
| --data-home=PATH | |
| Set the parent directory of the FRP data directory. | |
| Release data files are installed into: | |
| PATH/frp | |
| Defaults: | |
| user: ${XDG_DATA_HOME:-$HOME/.local/share} | |
| system: /usr/local/share | |
| --bin-dir PATH | |
| --bin-dir=PATH | |
| Install frpc and frps directly into PATH. | |
| Resulting executable paths: | |
| PATH/frpc | |
| PATH/frps | |
| Defaults: | |
| user: $HOME/.local/bin | |
| system: /usr/local/bin | |
| PATH cannot be empty. | |
| -h, --help | |
| Show this help. | |
| Supported Linux architectures: | |
| x86_64, amd64 | |
| aarch64, arm64 | |
| Default user layout: | |
| ${XDG_DATA_HOME:-$HOME/.local/share}/frp | |
| ${XDG_CONFIG_HOME:-$HOME/.config}/frp/frpc.toml | |
| ${XDG_CONFIG_HOME:-$HOME/.config}/frp/frps.toml | |
| $HOME/.local/bin/frpc | |
| $HOME/.local/bin/frps | |
| Default system layout: | |
| /usr/local/share/frp | |
| /usr/local/etc/frp/frpc.toml | |
| /usr/local/etc/frp/frps.toml | |
| /usr/local/bin/frpc | |
| /usr/local/bin/frps | |
| Examples: | |
| _install_frp | |
| _install_frp --user | |
| _install_frp --system | |
| _install_frp \ | |
| --data-home "$HOME/.local/share" \ | |
| --bin-dir "$HOME/.local/bin" | |
| _install_frp \ | |
| --system \ | |
| --data-home /usr/local/share \ | |
| --bin-dir /usr/local/bin | |
| Run the default user installation: | |
| frpc -c "${XDG_CONFIG_HOME:-$HOME/.config}/frp/frpc.toml" | |
| frps -c "${XDG_CONFIG_HOME:-$HOME/.config}/frp/frps.toml" | |
| Run the default system installation: | |
| frpc -c /usr/local/etc/frp/frpc.toml | |
| frps -c /usr/local/etc/frp/frps.toml | |
| Uninstall the default user installation: | |
| systemctl --user disable --now frpc 2>/dev/null || true | |
| systemctl --user disable --now frps 2>/dev/null || true | |
| rm -f -- "$HOME/.local/bin/frpc" "$HOME/.local/bin/frps" | |
| rm -rf -- "${XDG_DATA_HOME:-$HOME/.local/share}/frp" | |
| Also remove the user configuration: | |
| rm -rf -- "${XDG_CONFIG_HOME:-$HOME/.config}/frp" | |
| Uninstall the default system installation: | |
| sudo systemctl disable --now frpc 2>/dev/null || true | |
| sudo systemctl disable --now frps 2>/dev/null || true | |
| sudo rm -f -- /usr/local/bin/frpc /usr/local/bin/frps | |
| sudo rm -rf -- /usr/local/share/frp | |
| Also remove the system configuration: | |
| sudo rm -rf -- /usr/local/etc/frp | |
| HELP | |
| return 0 | |
| ;; | |
| --) | |
| shift | |
| if (($# > 0)); then | |
| echo "Unexpected positional argument: $1" >&2 | |
| return 2 | |
| fi | |
| ;; | |
| -*) | |
| echo "Unknown option: $1" >&2 | |
| return 2 | |
| ;; | |
| *) | |
| echo "Unexpected positional argument: $1" >&2 | |
| echo "Use --data-home or --bin-dir to specify paths" >&2 | |
| return 2 | |
| ;; | |
| esac | |
| done | |
| case "$scope" in | |
| user) | |
| if ((data_home_set == 0)); then | |
| data_home="${XDG_DATA_HOME:-$HOME/.local/share}" | |
| fi | |
| if ((bin_dir_set == 0)); then | |
| bin_dir="$HOME/.local/bin" | |
| fi | |
| config_home="${XDG_CONFIG_HOME:-$HOME/.config}/frp" | |
| binary_mode=700 | |
| data_dir_mode=700 | |
| config_dir_mode=700 | |
| bin_dir_mode=700 | |
| ;; | |
| system) | |
| if ((EUID != 0)); then | |
| echo "System installation requires root privileges" >&2 | |
| return 1 | |
| fi | |
| if ((data_home_set == 0)); then | |
| data_home="/usr/local/share" | |
| fi | |
| if ((bin_dir_set == 0)); then | |
| bin_dir="/usr/local/bin" | |
| fi | |
| config_home="/usr/local/etc/frp" | |
| binary_mode=755 | |
| data_dir_mode=755 | |
| config_dir_mode=755 | |
| bin_dir_mode=755 | |
| ;; | |
| esac | |
| if [[ -z $data_home ]]; then | |
| echo "Data home cannot be empty" >&2 | |
| return 2 | |
| fi | |
| if [[ -z $bin_dir ]]; then | |
| echo "Binary directory cannot be empty" >&2 | |
| return 2 | |
| fi | |
| data_home="${data_home%/}" | |
| bin_dir="${bin_dir%/}" | |
| frp_home="$data_home/frp" | |
| machine="$(uname -m)" | |
| case "$machine" in | |
| x86_64 | amd64) | |
| archive_arch="amd64" | |
| ;; | |
| aarch64 | arm64) | |
| archive_arch="arm64" | |
| ;; | |
| *) | |
| echo "Unsupported architecture: $machine" >&2 | |
| return 1 | |
| ;; | |
| esac | |
| tmp="$(mktemp -d)" || { | |
| echo "Failed to create a temporary directory" >&2 | |
| return 1 | |
| } | |
| new_frpc="$bin_dir/.frpc.new.$$" | |
| new_frps="$bin_dir/.frps.new.$$" | |
| latest_dir="$( | |
| curl -fsSL "$base/" \ | |
| | grep -oE \ | |
| '/projects/frp\.mirror/files/v[0-9]+\.[0-9]+\.[0-9]+/' \ | |
| | sort -Vu \ | |
| | tail -n 1 | |
| )" | |
| if [[ -z $latest_dir ]]; then | |
| echo "Failed to find the latest FRP release" >&2 | |
| else | |
| download_path="$( | |
| curl -fsSL "https://sourceforge.net$latest_dir" \ | |
| | grep -oE \ | |
| "/projects/frp\.mirror/files/v[0-9]+\.[0-9]+\.[0-9]+/frp_[0-9]+\.[0-9]+\.[0-9]+_linux_${archive_arch}\.tar\.gz/download" \ | |
| | head -n 1 | |
| )" | |
| if [[ -z $download_path ]]; then | |
| echo "Failed to find the Linux $archive_arch package" >&2 | |
| elif ! curl \ | |
| -fL \ | |
| "https://sourceforge.net$download_path" \ | |
| -o "$tmp/frp.tar.gz" | |
| then | |
| echo "Failed to download FRP" >&2 | |
| elif ! tar -xzf "$tmp/frp.tar.gz" -C "$tmp"; then | |
| echo "Failed to extract the FRP archive" >&2 | |
| else | |
| source_dir="$( | |
| find "$tmp" \ | |
| -mindepth 1 \ | |
| -maxdepth 1 \ | |
| -type d \ | |
| -name "frp_*_linux_${archive_arch}" \ | |
| -print \ | |
| -quit | |
| )" | |
| if [[ -z $source_dir ]]; then | |
| echo "Failed to locate the extracted FRP directory" >&2 | |
| elif [[ ! -f $source_dir/frpc ]]; then | |
| echo "The FRP archive does not contain frpc" >&2 | |
| elif [[ ! -f $source_dir/frps ]]; then | |
| echo "The FRP archive does not contain frps" >&2 | |
| elif ! install -d -m "$data_dir_mode" "$frp_home"; then | |
| echo "Failed to create the FRP data directory" >&2 | |
| elif ! install -d -m "$config_dir_mode" "$config_home"; then | |
| echo "Failed to create the FRP configuration directory" >&2 | |
| elif ! install -d -m "$bin_dir_mode" "$bin_dir"; then | |
| echo "Failed to create the executable directory" >&2 | |
| elif ! install \ | |
| -m "$binary_mode" \ | |
| "$source_dir/frpc" \ | |
| "$new_frpc" | |
| then | |
| echo "Failed to stage frpc" >&2 | |
| elif ! install \ | |
| -m "$binary_mode" \ | |
| "$source_dir/frps" \ | |
| "$new_frps" | |
| then | |
| echo "Failed to stage frps" >&2 | |
| else | |
| rc=0 | |
| while IFS= read -r -d '' file; do | |
| name="${file##*/}" | |
| case "$name" in | |
| frpc | frps) | |
| continue | |
| ;; | |
| frpc.toml | frps.toml) | |
| if [[ ! -e $config_home/$name && ! -L $config_home/$name ]]; then | |
| install -m 600 "$file" "$config_home/$name" || rc=1 | |
| fi | |
| ;; | |
| *) | |
| rm -rf -- "$frp_home/$name" \ | |
| && cp -a -- "$file" "$frp_home/$name" \ | |
| || rc=1 | |
| ;; | |
| esac | |
| done < <( | |
| find "$source_dir" \ | |
| -mindepth 1 \ | |
| -maxdepth 1 \ | |
| -print0 | |
| ) | |
| if ((rc == 0)); then | |
| mv -fT -- "$new_frpc" "$bin_dir/frpc" \ | |
| && mv -fT -- "$new_frps" "$bin_dir/frps" \ | |
| || rc=1 | |
| fi | |
| fi | |
| fi | |
| fi | |
| rm -rf -- "$tmp" | |
| rm -f -- "$new_frpc" "$new_frps" | |
| return "$rc" | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment