Skip to content

Instantly share code, notes, and snippets.

@1x24
Created April 27, 2023 07:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 1x24/9ca8e44c20a5351203058815c4be6a05 to your computer and use it in GitHub Desktop.
Save 1x24/9ca8e44c20a5351203058815c4be6a05 to your computer and use it in GitHub Desktop.
Easily mount a remote Linux server to MacOS using Fuse-T and SSHFS
#!/bin/bash
##################################################################################################################################################################
##################################################################################################################################################################
# https://github.com/1x24
# 1x24 AT tuta.io
# April 27, 2023
##################################################################################################################################################################
# This BASH script automatically mounts remote directories from a Linux server to your MacOS computer, using SSHFS.
# It is assumed that you have already set up passwordless login using SSH keys. If not, you can use https://gist.github.com/1x24/c96a93d3c749e996319ac6001ef4e3ab
# The script will terminate its process if you didn't set up SSH key-based login to the remote server that you're attempting to mount.
#
# The technology:
# FUSE is commonly used for such user-space purposes, but it requires you to accept Kernel Extensions (kext) in your OS, which could be a rather alarming idea.
# Enter the recent FUSE-T project https://www.fuse-t.org/ - a kext-less user-space FUSE for MacOS that uses an NFS v4 local server rather than a kernel extension.
# Note that FUSE-T is for non-commercial use, and under some conditions. See https://github.com/macos-fuse-t/fuse-t/blob/main/License.txt for more details.
# This script also uses SSHFS (https://github.com/macos-fuse-t/sshfs) which is GNU GPL v2.0
# Finally, to install both SSHFS and FUSE-T, this script uses Homebrew, the "missing package manager for MacOS" https://brew.sh/
#
# Requirements:
# Ensure you have Apple XCode installed. If you're not sure, run "xcode-select --install" in Terminal. This will start an XCode installation if necessary.
#
# Caveat:
# I have a Base-64 encoded string inside this script, but I explained my reason below, as well as included the decoded string for your inspection.
#
# USAGE:
# Simply download the script, and let's say you save it as `mount.sh`. Then run `chmod +x mount.sh` to make it executable and finally run `bash mount.sh`
# Enjoy!
###################################################################################################################################################################
###################################################################################################################################################################
#
# name of this script
script_name="$0";
# the user and their home (regardless of whether or not this is a sudo command)
orig_user="$(stat -f "%Su" /dev/console)";
orig_home="$(eval echo ~$orig_user)";
# important paths
private_key_path=""
mount_base_path="/Volumes/"
base_path="${orig_home}/.ssh"
ssh_config_path="${base_path}/config"
sshfs_config_path="${base_path}/.sshfs"
sshfs_install_ready_path="${base_path}/.sshfs_install_ready"
script_log_path="/tmp/$script_name.log"
# Clear out script install log file, or otherwise create it
echo -n "" >$script_log_path
# is homebrew ready?
is_brew_ready() { command -v "brew" >/dev/null 2>&1; }
# is the FUSE-T file system ready?
is_fuse_ready() { brew list "fuse-t" >/dev/null 2>&1; }
# is the SSHFS driver ready?
is_sshfs_ready() { brew list "fuse-t-sshfs" >/dev/null 2>&1; }
# add a line for remote_hostname into the file at $sshfs_config_path; create it if it doesn't exist.
add_sshfs_config(){
local remote_hostname="$1"; local remote_port="$2"; local remote_user="$3"; local private_key_path="$4"
[ ! -f "$sshfs_config_path" ] && touch "$sshfs_config_path"
echo "${remote_hostname}|${remote_user}|${remote_port}|${private_key_path}" >> "$sshfs_config_path"
}
# remove the line that starts with remote_hostname from the file at $sshfs_config_path, if the file exists.
remove_sshfs_config(){
local remote_hostname="$1"
[ -f "$sshfs_config_path" ] && sed -i.bak "/^${remote_hostname}|/d" "$sshfs_config_path" && rm "$sshfs_config_path.bak"
}
# fetch the remote_port, username and private key path from the host definition for $remote_hostname inside $ssh_config_path
fetch_ssh_config() {
local remote_hostname="$1"
[ -f "$ssh_config_path" ] && awk -v host="$remote_hostname" '
$1 == "Host" { current_host = $2 }
current_host == host && $1 == "User" { user = $2 }
current_host == host && $1 == "Port" { port = $2 }
current_host == host && $1 == "IdentityFile" { idfile = $2 }
END { if (user) print user "|" (port ? port : 22) "|" idfile }
' "$ssh_config_path"
}
# test this SSH key $key_path to confirm if it can log $remote_user into $remote_hostname at port $remote_port
test_ssh_key(){
local remote_hostname=$1; local remote_port=$2; local remote_user=$3; local key_path=$4;
sshOptions=(-o PasswordAuthentication=no -o PubkeyAuthentication=yes -o PreferredAuthentications=publickey -o StrictHostKeyChecking=no -o BatchMode=yes -o ConnectTimeout=5)
ssh "${sshOptions[@]}" -p "$remote_port" -i "$key_path" -q "$remote_user@$remote_hostname" "exit" >/dev/null 2>&1
}
# find SSH keys in the default location (inside .ssh within the user's home)
find_default_ssh_keys(){
local key_paths=""
local possible_key_paths=("${base_path}/id_ed25519" "${base_path}/id_rsa")
for key_path in "${possible_key_paths[@]}"; do
[ -f "$key_path" ] && key_paths="${key_paths},${key_path}"
done
echo "${key_paths#,}"
}
# connect to the remote server and fetch all eligible remote directories as a comma separated list of items which are each directory path piped to its group name
##########################################################################################################################################
###### This part retrieves the directories that this remote user is eligible to mount. The directories are sourced from three ######
###### locations, and then automatically mounted. ######
###### (1) /etc/ssh/sshfs_config owned by root with permissions 644. Comments begin with #. Each line has form <group_name>:<path> ######
###### meaning all users in <group_name> are eligible to mount the directory <path>. If remote user belongs to the group, that ######
###### path will be included as an eligible path. This specific config file is, of course, only managed by a server admin. ######
###### (2) ~/.sshfs is a file inside the remote user's home directory, owned by the remote user. Each non-comment line contains a ######
###### <path> and as long as the user has read-write access to that path, it will be included as an eligible path. ######
###### (3) Regardless of the absence or presence of the first two config files, the script will always mount the remote user's ######
###### home directory. And if the user's home directory was already included by earlier configs, only one home will mount. ######
###### P.S. The script retrieves the groups as well, though they are not currently in use for this version of the script. ######
##########################################################################################################################################
get_remote_folders_list(){
### Base64 of the remote BASH script
local bash_script_base64='IyBzZXQgdGhlIGdyb3VwX2RpcnMgdmFyaWFibGUgYnkgbG9va2luZyBmb3IgZGlyZWN0b3JpZXMgaW4gL2V0Yy9zc2gvc3NoZnNfY29uZmlnIHRoYXQgdGhlIHVzZXIgaGFzIHBlcm1pc3Npb24gdG8gcmVhZAojIGlnbm9yZXMgYWxsIGNvbW1lbnQgbGluZXMgKHRob3NlIGJlZ2luIHdpdGggYSAjKSBhbmQgZW1wdHkgbGluZXMKZ3JvdXBfZGlycz0kKCAKICBbIC1mIC9ldGMvc3NoL3NzaGZzX2NvbmZpZyBdICYmIGF3ayAtRjogXAogICAgLXYgZ3JvdXBzPSIkKGlkIC1HbikiIFwKICAgIC12IHVzZXI9IiRVU0VSIiBcCiAgICAnQkVHSU4geyBPUlM9IiwiIH0gXAogICAgIC9eW14jXG5dLyB7IGlmIChpbmRleChncm91cHMsICQxKSAmJiBzeXN0ZW0oInRlc3QgLXIgIiAkMikgPT0gMCkgcHJpbnQgJDIgInwiICQxIH0nIFwKICAgIC9ldGMvc3NoL3NzaGZzX2NvbmZpZyB8fCBlY2hvICcnCikKCiMgc2V0IHRoZSBjdXN0b21fZGlycyB2YXJpYWJsZSBieSBsb29raW5nIGZvciBkaXJlY3RvcmllcyBpbiB+Ly5zc2hmcyB0aGF0IHRoZSB1c2VyIGhhcyBwZXJtaXNzaW9uIHRvIHJlYWQKIyBpZ25vcmVzIGFsbCBjb21tZW50IGxpbmVzICh0aG9zZSBiZWdpbiB3aXRoIGEgIykgYW5kIGVtcHR5IGxpbmVzCmN1c3RvbV9kaXJzPSQoIAogIFsgLWYgIiRIT01FLy5zc2hmcyIgXSAmJiBhd2sgXAogICAgLXYgdXNlcj0iJFVTRVIiIFwKICAgIC12IGdyb3Vwcz0iJChpZCAtRykiIFwKICAgICdCRUdJTiB7IE9SUz0iLCIgfSBcCiAgICAgL15bXiNcbl0vIHsgcGF0aD0kMDsgZ3JvdXA9Z2Vuc3ViKC8uKlwvKFteXC9dKylcL1teXC9dKyQvLCAiXFwxIiwgImciLCBwYXRoKTsgXAogICAgICAgaWYgKHN5c3RlbSgidGVzdCAtciAiIHBhdGgpID09IDApIHByaW50IHBhdGggInwiIGdyb3VwIH0nIFwKICAgICIkSE9NRS8uc3NoZnMiIHx8IGVjaG8gJycKKQoKIyBpZiBjdXN0b21fZGlycyBpcyBub3QgZW1wdHksIHJlcGxhY2UgdGhlIGdyb3VwIG5hbWVzIHdpdGggdGhlIGFjdHVhbCBncm91cCBuYW1lcyB1c2luZyBzdGF0IC1jICIlRyIKaWYgWyAtbiAiJGN1c3RvbV9kaXJzIiBdOyB0aGVuCiAgY3VzdG9tX2RpcnNfd2l0aF9ncm91cD0iIgogIElGUz0iLCIKICBmb3IgZGlyX2luZm8gaW4gJGN1c3RvbV9kaXJzOyBkbwogICAgZGlyPSQoZWNobyAiJGRpcl9pbmZvIiB8IGN1dCAtZCJ8IiAtZjEpCiAgICBncm91cD0kKHN0YXQgLWMgIiVHIiAiJGRpciIpCiAgICBjdXN0b21fZGlyc193aXRoX2dyb3VwPSIkY3VzdG9tX2RpcnNfd2l0aF9ncm91cCwkZGlyfCRncm91cCIKICBkb25lCiAgY3VzdG9tX2RpcnM9JChlY2hvICIkY3VzdG9tX2RpcnNfd2l0aF9ncm91cCIgfCBzZWQgInMvXiwvLyIpCmZpCgojIGNvbmNhdGVuYXRlIGdyb3VwX2RpcnMgYW5kIGN1c3RvbV9kaXJzCmFsbF9kaXJzPSIkZ3JvdXBfZGlycywkY3VzdG9tX2RpcnMiCgojIHJlbW92ZSBkaXJlY3RvcmllcyB0aGF0IHN0YXJ0IHdpdGggcGF0aHNfdG9fZXhjbHVkZQpwYXRoc190b19leGNsdWRlPSIvcm9vdCwvc3lzLC9ydW4sL2RldiwvYm9vdCwvcHJvYywvc2JpbiwvdXNyL3NiaW4sL3Vzci9iaW4sL3Vzci9sb2NhbCwvdG1wLC9ldGMsL3ZhciIKSUZTPSIsIgpmb3IgZXhjbHVkZV9wYXRoIGluICRwYXRoc190b19leGNsdWRlOyBkbwogIGFsbF9kaXJzPSQoZWNobyAiJGFsbF9kaXJzIiB8IHNlZCAicyxcKCRleGNsdWRlX3BhdGhbXix8XSp8W14sXSpcKSwsZyIpCmRvbmUKCiMgaW5jbHVkZSB0aGUgaG9tZV9kaXIgdmFyaWFibGUgdG8gdGhlIHVzZXIncyBob21lIGRpcmVjdG9yeSBhbmQgcHJpbWFyeSBncm91cCBuYW1lCmhvbWVfZGlyPSIvaG9tZS8kVVNFUnwkKGlkIC1nbiAkVVNFUikiCmFsbF9kaXJzPSIkYWxsX2RpcnMsJGhvbWVfZGlyIgoKIyBpbmNsdWRlIHBhdGhzIGZyb20gcGF0aHNfdG9faW5jbHVkZSBpZiB0aGV5IGFyZSByZWFkYWJsZSBhbmQgcHJlc2VudCBpbiBncm91cF9kaXJzIG9yIGN1c3RvbV9kaXJzCnBhdGhzX3RvX2luY2x1ZGU9Ii92YXIvd3d3IgpJRlM9IiwiCmZvciBpbmNsdWRlX3BhdGggaW4gJHBhdGhzX3RvX2luY2x1ZGU7IGRvCiAgaW5jbHVkZWRfcGF0aD0kKGVjaG8gIiRncm91cF9kaXJzLCRjdXN0b21fZGlycyIgfCBncmVwIC1vICIkaW5jbHVkZV9wYXRoW14sfF0qfFteLF0qIikKICBpZiBbIC1uICIkaW5jbHVkZWRfcGF0aCIgXTsgdGhlbgogICAgZGlyX3RvX2NoZWNrPSIkKGVjaG8gIiRpbmNsdWRlZF9wYXRoIiB8IGN1dCAtZCJ8IiAtZjEpIgogICAgaWYgdGVzdCAtciAiJGRpcl90b19jaGVjayI7IHRoZW4KICAgICAgYWxsX2RpcnM9IiRhbGxfZGlycywkaW5jbHVkZWRfcGF0aCIKICAgIGZpCiAgZmkKZG9uZQoKIyBjb25jYXRlbmF0ZSB0aGUgZmlsdGVyZWQgbGlzdCB3aXRoIGhvbWVfZGlyIGFuZCBmb3JtYXQgdGhlIG91dHB1dAplY2hvICIkYWxsX2RpcnMiIHwgXAogIHNlZCAicy8sXFwrLywvZzsgcy9eLC8vOyBzLyxcJC8vIiB8IFwKICB0ciAiLCIgIlxuIiB8IFwKICBzb3J0IC11IHwgXAogIHRyICJcbiIgIiwiIHwgXAogIHNlZCAicy9eW1x8LF0qLy87cy9bXHwsXSokLy8i'
local remoteCommand="echo $bash_script_base64 | base64 --decode | bash 2>/dev/null";
########################################################################################################################################
###### Okay, so what's all this gibberish above? It's simply a base64 representation of a BASH script to be sent server-side. ######
###### I decided to keep things uniform between MacOS/Linux client-side MacOS (BASH) and client-side Windows Powershell scripts, ######
###### but sadly, Powershell can't handle all the various forms of character escapes that BASH can, and so this was the best way. ######
###### See the bottom of this file for the decoded BASH script. ######
###### Don't take my word for it, though. You can confirm with a base64 de-converter e.g. https://www.base64decode.org/ ######
########################################################################################################################################
local remote_hostname="$1"; local remote_port="$2"; local remote_user="$3"; local private_key_path="$4";
local sshOptions=(-o PasswordAuthentication=no -o PubkeyAuthentication=yes -o PreferredAuthentications=publickey -o StrictHostKeyChecking=no)
echo $(ssh "${sshOptions[@]}" -p "$remote_port" -i "$private_key_path" "$remote_user@$remote_hostname" "$remoteCommand")
}
# mount a specific remote folder using SSHFS
mount_remote_folder(){
local remote_hostname="$1"; local remote_port="$2"; local remote_user="$3"; local private_key_path="$4"; local remote_data="$5"
local remote_dir_path=$(echo "$remote_data" | cut -d'|' -f1)
local remote_group=$(echo "$remote_data" | cut -d'|' -f2)
local dir_name=$(basename "$remote_dir_path")
local nfs_mountpoint="${mount_base_path}${remote_hostname}/${dir_name}"
# Check if the local mount point exists and is already mounted
if mount | grep -q "$nfs_mountpoint"; then
echo "Skipping directory $remote_dir_path since it already exists at $nfs_mountpoint..."; return
fi
# Check if the local mount point exists. Create it if not, and set ownership with 755 permissions
if [ ! -d "$nfs_mountpoint" ]; then
echo "Creating local mount point directory...";
fi
sudo mkdir -m 775 -p "$nfs_mountpoint" >>$script_log_path 2>&1
sudo chown "$orig_user" "$nfs_mountpoint" >>$script_log_path 2>&1
# Mount the remote directory using SSHFS as the original user
sshfs -p "$remote_port" "$remote_user@$remote_hostname:$remote_dir_path" "$nfs_mountpoint" -o rw,reconnect,default_permissions,volname="${dir_name}",IdentityFile="$private_key_path",follow_symlinks,ServerAliveInterval=15,ServerAliveCountMax=3 >>$script_log_path 2>&1
if [ $? -eq 0 ]; then
# Reset permissions
sudo chmod 775 "$nfs_mountpoint" >>$script_log_path 2>&1
sudo chown "$orig_user" "$nfs_mountpoint" >>$script_log_path 2>&1
# Mount was successful. Append command to automounter script
echo "Remote directory $remote_dir_path mounted at $nfs_mountpoint"
else
# Mount failed.
echo "Mounting directory $remote_dir_path failed. Skipping this mount..."
fi
}
# mount all remote folders that this remote user is eligible to read and write, AND have also been assigned to them
mount_eligible_folders() {
local remote_hostname="$1"; local remote_port="$2"; local remote_user="$3"; local private_key_path="$4";
local remote_data_string=$(get_remote_folders_list "$remote_hostname" "$remote_port" "$remote_user" "$private_key_path");
IFS=',' read -ra remote_data_array <<< "$remote_data_string";
# Mount all remote data
local remote_data=""
for remote_data in "${remote_data_array[@]}"; do
mount_remote_folder "$remote_hostname" "$remote_port" "$remote_user" "$private_key_path" "$remote_data"
sleep 1
done
}
# check for SSH keys in the default directory, and see if any of them work for this remote server
try_default_keys() {
local remote_hostname="$1"
local possible_keys=$(find_default_ssh_keys)
local remote_port=""
local remote_user=""
if [ -z "$possible_keys" ]; then
echo "Error: No valid SSH keys were found."
exit 1
else
IFS=',' read -ra possible_keys_array <<< "$possible_keys"
read -rp "Enter the remote SSH port [22]: " -e remote_port
remote_port="${remote_port:-22}"
read -rp "Enter your username on the $remote_hostname server: " remote_user
local possible_key=""
for possible_key in "${possible_keys_array[@]}"; do
if test_ssh_key "$remote_hostname" "$remote_port" "$remote_user" "$possible_key"; then
private_key_path="$possible_key"
add_sshfs_config "$remote_hostname" "$remote_port" "$remote_user" "$private_key_path"
mount_eligible_folders "$remote_hostname" "$remote_port" "$remote_user" "$private_key_path"
break
fi
done
if [ -z "$private_key_path" ]; then
echo "Error: No valid SSH keys were found."
exit 1
fi
fi
}
# attempt to use information from the ssh config file, as much as possible
use_ssh_config(){
local remote_hostname="$1"
local result=$(fetch_ssh_config "$remote_hostname")
if [ -z "$result" ]; then
try_default_keys $remote_hostname
else
IFS='|' read -ra config <<< "$result"
local remote_user="${config[0]}"
local remote_port="${config[1]}"
local private_key_path="${config[2]}"
if test_ssh_key "$remote_hostname" "$remote_port" "$remote_user" "$private_key_path"; then
add_sshfs_config "$remote_hostname" "$remote_port" "$remote_user" "$private_key_path"
mount_eligible_folders "$remote_hostname" "$remote_port" "$remote_user" "$private_key_path"
else
echo "Error: Invalid SSH key"
exit 1
fi
fi
}
# attempt to connect automatically using previously saved configuration details, if those exist
try_sshfs_config() {
if [ -s "$sshfs_config_path" ]; then
while read -r config_line; do
IFS='|' read -ra config <<< "$config_line"
local remote_hostname="${config[0]}"
local remote_user="${config[1]}"
local remote_port="${config[2]}"
local private_key_path="${config[3]}"
echo "Server: $remote_hostname"
if test_ssh_key "$remote_hostname" "$remote_port" "$remote_user" "$private_key_path"; then
mount_eligible_folders "$remote_hostname" "$remote_port" "$remote_user" "$private_key_path"
else
echo "Error: Invalid SSH key"
exit 1
fi
done < "$sshfs_config_path"
else
local remote_hostname=""
read -rp "Enter remote host: " remote_hostname
use_ssh_config "$remote_hostname"
fi
}
# are all installation requirements ready?
is_install_ready(){
if [ -f "$sshfs_install_ready_path" ]; then
return
else
if is_brew_ready; then
echo "Homebrew package manager is already installed."
else
echo "Homebrew package manager not found. Installing Homebrew."
while true; do echo -n '.'; sleep 1; done & bg_pid=$!; disown $bg_pid
NONINTERACTIVE=1 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" >>$script_log_path 2>&1
kill $bg_pid; (echo; echo 'eval "$(/opt/homebrew/bin/brew shellenv)"') >> "$orig_home/.bash_profile"
eval "$(/opt/homebrew/bin/brew shellenv)"; echo "Done"
fi
if is_fuse_ready; then
echo "MacOS-FUSE-T for Homebrew is already installed."
else
echo "MacOS-FUSE-T for Homebrew not found. Installing MacOS-FUSE-T."
while true; do echo -n '.'; sleep 1; done & bg_pid=$!; disown $bg_pid
brew tap macos-fuse-t/homebrew-cask >>$script_log_path 2>&1
brew install fuse-t >>$script_log_path 2>&1
kill $bg_pid; echo "Done"
fi
if is_sshfs_ready; then
echo "SSHFS for FUSE-T is already installed."
else
echo "SSHFS not found. Installing SSHFS for FUSE-T."
while true; do echo -n '.'; sleep 1; done & bg_pid=$!; disown $bg_pid
brew install fuse-t-sshfs >>$script_log_path 2>&1
kill $bg_pid; echo "Done"
fi
echo $(is_brew_ready && is_fuse_ready && is_sshfs_ready)
fi
}
# main driver function
main() {
local cmd=""
local required_commands=("awk" "sed" "curl" "stat" "eval" "sleep" "disown" "kill" "ssh" "cut" "basename" "cut" "mount" "grep" "read")
for cmd in "${required_commands[@]}"; do
command -v "$cmd" > /dev/null || { echo "Error: $cmd is not available in your shell"; exit 1; }
done
echo "###################################################################################################"
echo "To setup things seamlessly, this script may ask you to provide the MacOS password for $orig_user."
export SUDO_PROMPT="MacOS account password for $orig_user: " # apply custom sudo prompt (for clarity)
sudo -v # ask for sudo upfront and avoid hiccups
export SUDO_PROMPT="Password: " # reset default sudo prompt
echo "###################################################################################################"
if (! is_install_ready); then
echo "Error: some installation requirements are missing. Please contact the administrator."
exit 1
else
touch "${sshfs_install_ready_path}"
try_sshfs_config
fi
}
main
################# The decoded remote-side BASH script #################
#################
### This code finds directories that the user has read and write permissions to, and prints them along with their corresponding group names.
### The output is a comma-separated list of directories and group names. The sed and tr commands at the end of the script clean up the output
### by removing any extra commas and formatting the output as a comma-separated list.
### P.S. The script retrieves the groups as well, though they are not currently in use for this version of the script.
#################
# decoded string begins below, between the `remote_script_decoded` heredocs
:<<'remote_script_decoded'
# set the group_dirs variable by looking for directories in /etc/ssh/sshfs_config that the user has permission to read
# ignores all comment lines (those begin with a #) and empty lines
group_dirs=$(
[ -f /etc/ssh/sshfs_config ] && awk -F: \
-v groups="$(id -Gn)" \
-v user="$USER" \
'BEGIN { ORS="," } \
/^[^#\n]/ { if (index(groups, $1) && system("test -r " $2) == 0) print $2 "|" $1 }' \
/etc/ssh/sshfs_config || echo ''
)
# set the custom_dirs variable by looking for directories in ~/.sshfs that the user has permission to read
# ignores all comment lines (those begin with a #) and empty lines
custom_dirs=$(
[ -f "$HOME/.sshfs" ] && awk \
-v user="$USER" \
-v groups="$(id -G)" \
'BEGIN { ORS="," } \
/^[^#\n]/ { path=$0; group=gensub(/.*\/([^\/]+)\/[^\/]+$/, "\\1", "g", path); \
if (system("test -r " path) == 0) print path "|" group }' \
"$HOME/.sshfs" || echo ''
)
# if custom_dirs is not empty, replace the group names with the actual group names using stat -c "%G"
if [ -n "$custom_dirs" ]; then
custom_dirs_with_group=""
IFS=","
for dir_info in $custom_dirs; do
dir=$(echo "$dir_info" | cut -d"|" -f1)
group=$(stat -c "%G" "$dir")
custom_dirs_with_group="$custom_dirs_with_group,$dir|$group"
done
custom_dirs=$(echo "$custom_dirs_with_group" | sed "s/^,//")
fi
# concatenate group_dirs and custom_dirs
all_dirs="$group_dirs,$custom_dirs"
# remove directories that start with paths_to_exclude
paths_to_exclude="/root,/sys,/run,/dev,/boot,/proc,/sbin,/usr/sbin,/usr/bin,/usr/local,/tmp,/etc,/var"
IFS=","
for exclude_path in $paths_to_exclude; do
all_dirs=$(echo "$all_dirs" | sed "s,\($exclude_path[^,|]*|[^,]*\),,g")
done
# include the home_dir variable to the user's home directory and primary group name
home_dir="/home/$USER|$(id -gn $USER)"
all_dirs="$all_dirs,$home_dir"
# include paths from paths_to_include if they are readable and present in group_dirs or custom_dirs
paths_to_include="/var/www"
IFS=","
for include_path in $paths_to_include; do
included_path=$(echo "$group_dirs,$custom_dirs" | grep -o "$include_path[^,|]*|[^,]*")
if [ -n "$included_path" ]; then
dir_to_check="$(echo "$included_path" | cut -d"|" -f1)"
if test -r "$dir_to_check"; then
all_dirs="$all_dirs,$included_path"
fi
fi
done
# concatenate the filtered list with home_dir and format the output
echo "$all_dirs" | \
sed "s/,\\+/,/g; s/^,//; s/,\$//" | \
tr "," "\n" | \
sort -u | \
tr "\n" "," | \
sed "s/^[\|,]*//;s/[\|,]*$//"
remote_script_decoded
####################### End of BASH script ###########################
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment