Last active
July 22, 2025 03:20
-
-
Save lsr00ter/816486fe6d317de22b83801ca7977e1b to your computer and use it in GitHub Desktop.
Mount an NFS share on macOS gracefully to ensure seamless and reliable access.
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
| #!/bin/bash | |
| # Default values | |
| FILE="/etc/nfs.conf" | |
| LINE="nfs.client.mount.options = vers=4" | |
| REMOTE_SERVER="" | |
| SERVER_DIR="" | |
| MOUNTED_DIR="" | |
| UNMOUNT=false | |
| # Function to display usage | |
| usage() { | |
| echo "Usage: $0 [-s <remote_server>] [-m <mount_dir>] [-f <config_file>] [-l <line>] [-u]" | |
| echo " -s: Remote NFS server (e.g., 192.168.1.100)" | |
| echo " -m: Local mount directory (e.g., /mnt/nfs)" | |
| echo " -f: NFS configuration file (default: /etc/nfs.conf)" | |
| echo " -l: Line to append (default: nfs.client.mount.options = vers=4)" | |
| echo " -u: Unmount the NFS share instead of mounting" | |
| exit 1 | |
| } | |
| # Function to check if running as root | |
| check_root() { | |
| if [[ $EUID -ne 0 ]]; then | |
| echo "Error: This operation requires root privileges. Please run with sudo." | |
| exit 1 | |
| fi | |
| } | |
| # Function to validate IP or hostname | |
| validate_server() { | |
| local server=$1 | |
| if [[ -z "$server" ]]; then | |
| echo "Error: Remote server cannot be empty." | |
| exit 1 | |
| fi | |
| if ! ping -c 1 -W 2 "$server" &>/dev/null; then | |
| echo "Error: Cannot reach server $server. Check network or server address." | |
| exit 1 | |
| fi | |
| } | |
| # Function to prompt for input if variable is empty | |
| prompt_if_empty() { | |
| local var_name=$1 | |
| local prompt_msg=$2 | |
| local var_value=${!var_name} | |
| if [[ -z "$var_value" ]]; then | |
| read -e -p "$prompt_msg: " input | |
| input="${input/#\~/$HOME}" | |
| eval "$var_name='$input'" | |
| fi | |
| } | |
| # Function to select NFS export from showmount | |
| select_nfs_export() { | |
| local server=$1 | |
| echo "Fetching NFS exports from $server..." | |
| exports=$(showmount -e "$server" | tail -n +2) | |
| if [[ -z "$exports" ]]; then | |
| echo "Error: No exports found on $server." | |
| exit 1 | |
| fi | |
| # Convert exports to array, preserving spaces | |
| IFS=$'\n' read -d '' -r -a export_array <<< "$exports" | |
| echo "Available NFS exports:" | |
| for i in "${!export_array[@]}"; do | |
| # Extract the full path, including spaces, before the client list | |
| path=$(echo "${export_array[$i]}" | sed 's/^[[:space:]]*\(.*\)[[:space:]]\+.*$/\1/') | |
| echo "[$i] $path" | |
| done | |
| # Prompt for selection | |
| while true; do | |
| read -p "Select an export by number (0-${#export_array[@]}-1): " choice | |
| if [[ "$choice" =~ ^[0-9]+$ && "$choice" -ge 0 && "$choice" -lt "${#export_array[@]}" ]]; then | |
| # Extract the selected path, including spaces | |
| SERVER_DIR=$(echo "${export_array[$choice]}" | sed 's/^[[:space:]]*\(.*\)[[:space:]]\+.*$/\1/') | |
| break | |
| else | |
| echo "Invalid choice. Please enter a number between 0 and ${#export_array[@]}-1." | |
| fi | |
| done | |
| } | |
| # Parse command-line arguments | |
| while getopts "s:m:f:l:u" opt; do | |
| case $opt in | |
| s) REMOTE_SERVER="$OPTARG" ;; | |
| m) MOUNTED_DIR="$OPTARG" ;; | |
| f) FILE="$OPTARG" ;; | |
| l) LINE="$OPTARG" ;; | |
| u) UNMOUNT=true ;; | |
| *) usage ;; | |
| esac | |
| done | |
| # Prompt for missing values if not provided | |
| prompt_if_empty "REMOTE_SERVER" "Enter remote NFS server (e.g., 192.168.1.100)" | |
| prompt_if_empty "MOUNTED_DIR" "Enter local mount directory (e.g., /mnt/nfs)" | |
| # Validate inputs | |
| if [[ ! -f "$FILE" && ! $UNMOUNT ]]; then | |
| echo "Error: Configuration file $FILE does not exist." | |
| exit 1 | |
| fi | |
| validate_server "$REMOTE_SERVER" | |
| if [[ -z "$MOUNTED_DIR" || ! "$MOUNTED_DIR" =~ ^/ ]]; then | |
| echo "Error: Mount directory must be a valid absolute path (e.g., /mnt/nfs)." | |
| exit 1 | |
| fi | |
| # Select NFS export if mounting | |
| if ! $UNMOUNT; then | |
| select_nfs_export "$REMOTE_SERVER" | |
| if [[ -z "$SERVER_DIR" || ! "$SERVER_DIR" =~ ^/ ]]; then | |
| echo "Error: Selected server directory is invalid." | |
| exit 1 | |
| fi | |
| fi | |
| # Unmount logic | |
| if $UNMOUNT; then | |
| check_root | |
| if grep -qE "^${REMOTE_SERVER}:${SERVER_DIR// /\\ } ${MOUNTED_DIR// /\\ } " /proc/mounts; then | |
| if sudo umount "$MOUNTED_DIR"; then | |
| echo "Successfully unmounted $MOUNTED_DIR." | |
| sudo rmdir "$MOUNTED_DIR" 2>/dev/null && echo "Removed mount directory $MOUNTED_DIR." | |
| else | |
| echo "Error: Failed to unmount $MOUNTED_DIR." | |
| exit 1 | |
| fi | |
| else | |
| echo "Error: $MOUNTED_DIR is not a mount point." | |
| exit 1 | |
| fi | |
| .................................................................................. | |
| exit 0 | |
| fi | |
| # Check and append configuration line | |
| CONFIG_MODIFIED=false | |
| if ! grep -Fx "$LINE" "$FILE" > /dev/null; then | |
| check_root | |
| if echo "$LINE" >> "$FILE"; then | |
| echo "Line appended to $FILE: $LINE" | |
| CONFIG_MODIFIED=true | |
| else | |
| echo "Error: Failed to append $LINE to $FILE." | |
| exit 1 | |
| fi | |
| fi | |
| # Restart NFS service if configuration was modified | |
| if $CONFIG_MODIFIED; then | |
| if sudo systemctl restart nfs-client.target 2>/dev/null || systemctl restart nfs-utils.service 2>/dev/null; then | |
| echo "NFS client service restarted." | |
| else | |
| echo "Warning: Failed to restart NFS service. Changes may not take effect until reboot." | |
| fi | |
| fi | |
| # Create mount directory | |
| check_root | |
| if [[ ! -d "$MOUNTED_DIR" ]]; then | |
| if sudo mkdir -p "$MOUNTED_DIR"; then | |
| echo "Created mount directory $MOUNTED_DIR." | |
| else | |
| echo "Error: Failed to create mount directory $MOUNTED_DIR." | |
| exit 1 | |
| fi | |
| elif [[ -n "$(ls -A "$MOUNTED_DIR")" ]]; then | |
| echo "Error: Mount directory $MOUNTED_DIR is not empty." | |
| exit 1 | |
| fi | |
| # Mount NFS share | |
| if sudo mount -o rw,vers=4 -t nfs "$REMOTE_SERVER:$SERVER_DIR" "$MOUNTED_DIR"; then | |
| echo "Successfully mounted $REMOTE_SERVER:$SERVER_DIR on $MOUNTED_DIR." | |
| else | |
| echo "Error: Failed to mount $REMOTE_SERVER:$SERVER_DIR on $MOUNTED_DIR." | |
| exit 1 | |
| fi | |
| # Verify mount | |
| if grep -qE "^${REMOTE_SERVER}:${SERVER_DIR// /\\ } ${MOUNTED_DIR// /\\ } " /proc/mounts && df -h "$MOUNTED_DIR" > /dev/null; then | |
| echo "Mount verified. Details:" | |
| df -h "$MOUNTED_DIR" | |
| else | |
| echo "Warning: Mount appears incomplete or inaccessible." | |
| exit 1 | |
| fi |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment