Bash Script - Check mounted devices - linux

What I'm trying to do -
In the end, I want a script that checks for 3 devices, an SD card, Backup1 & Backup2. They've all been set to auto-mount at their respective mountpoints. The script should check for SD card first, if this fails, then a warning should be sent, and nothing more. If SD is okay, but only one backup is mounted, then ask for confirmation to go ahead with rsync to mounted backup. If all devices are mounted, then rsync from SD card to both backups.
Currently I'm just trying to get the device check nailed, using echo commands. Here's what I have (after multiple attempts) -
if ! mount | grep /media/card >/dev/null
then
echo "ERROR: SD card not mounted, aborting"
else
if ! mount | grep /media/backup >/dev/null
then
if ! mount | grep /media/backup2 >/dev/null
then
echo "ERROR: No backup devices"
else
echo "CAUTION: Backup_2 missing, Backup_1 OKAY"
fi
else
if ! mount | grep /media/backup2 /dev/null
then
echo "CAUTION: Backup_1 missing, Backup_2 OKAY"
else
echo "SUCCESS: All devices OKAY"
fi
fi
fi
Which isn't working. I'm getting confused by all the nested 'if's and surely there's a simpler way? Perhaps something that checks each device independently, then returns values that equate to the various combinations (0=no devices, 1=sd only, 2=sd & backup1, 3=sd & backup 2, 4 = all okay) which then is read and decides next part of script to run? If not, where has this way gone wrong?
Any help is appreciated

Use mountpoint, which returns success (0) if the path is a mounted device, and 1 otherwise:
#!/bin/bash
check_mountpoint(){
if mountpoint -q $1
then
printf "%s\n" "$1 is mounted"
return 0
else
printf "%s\n" "$1 is not mounted"
return 1
fi
}
check_mountpoint /media/sd
if [ $? -gt 0 ]
then
printf "%s\n" "aborting"
exit 0
fi
check_mountpoint /media/backup1
check_mountpoint /media/backup2
this is your code corrected, i hope it will help.
if mountpoint -q /media/sd
then
if mountpoint -q /media/backup
then
if mountpoint -q /media/backup2
then
echo "SUCCESS: All devices OKAY"
# run your command here
else
echo "CAUTION: Backup_2 missing, Backup_1 OKAY, SD OKAY"
# run your command here
fi
else
if mountpoint -q /media/backup2
then
echo "CAUTION: Backup_1 missing, Backup_2 OKAY, SD OKAY"
# run your command here
else
echo "SD OKAY , BACKUP Devices missing"
fi
fi
else
echo "SD is missing , Exit"
fi

Solved! Part of the problem was using mount | grep /media/backup > /dev/null as there's an issue with similar names, so /media/backup2 being mounted meant it was counting both devices as mounted.
Using grep -q /media/backup2\ /proc/mounts works in a similar way, or at least produces the same outcome, just without the naming issue.
I've also changed the way the if statements work, it now checks for SD card, if that fails, then it aborts. It then checks for which devices are mounted with a fairly simple flow.
Here's the code:
GNU nano 2.2.6 File: backupSD2.sh Modified
#!/bin/bash
#Check for SD
if ! grep -q /media/card\ /proc/mounts
then
echo "ERROR: NO SD CARD"
exit 0
else
echo "SD OKAY..."
fi
#Check for Backup Devices
if (( grep -q /media/backup\ /proc/mounts ) && ( grep -q /media/backup2\ /proc/mounts ))
then
echo "Both Backups mounted"
elif grep -q /media/backup\ /proc/mounts
then
echo "CAUTION: Backup_2 missing, Backup_1 OKAY"
elif grep -q /media/backup2\ /proc/mounts
then
echo "CAUTION: Backup_1 missing, Backup_2 OKAY"
else
echo "ERROR: NO BACKUP DEVICES"
exit 0
fi
After the 'echo' commands is where I'll be putting the rsync commands.
The final exit 0 is probably unnecessary, but I don't think it's doing any harm and reassures that it's not going to try any more commands.

Check for 1 required mounted source AND at least 1 required mounted target
The best aproach for this is to build an array of mounted devices, then evaluate de count of elements in array.
Introduction
1. Care! Using grep -q "$dev" /proc/mounts will fail to found spaced mount points!
/media/spaced target
in /proc/mounts will appear as
/media/spaced\040target
command printf -v varname '%b' $varname could help!
2. Using bash array and associative array to avoid fork
Instead of doing many fork to grep, (browsing same file many time), you could use bash for the job.
3. Parallelizing rsync on many targets
Using backgrounded jobs and wait -n -p <VARNAME>, you could do rsync on many targets simultaneously.
4. Using /proc/self/mounts instead of /proc/mounts
After having a look at man 5 proc.
Here is a script, within:
you could configure as many backup targets you want.
if all targets are mounted, script go on
if no targets are mounted, script stop and
if at least one target is mounted but not all, user is asked for continuing.
(this could be avoided by using -y/-n optional flag)
On second part, all rsync are run as background job (parallelized),
then script wait for all rsync to be done,
then print each result code with corresponding target,
before finish
The script:
#!/bin/bash
source='/media/card'
backups=(
/media/backup
/media/backup2
/media/other\ backup
)
die() {
echo "$0[$$] ERROR: $#" >&2
exit 1
}
warn() {
echo "$0[$$] WARNING: $#" >&2
}
declare -A tobemounted="(['$source']='source')"
for target in "${backups[#]}";do
tobemounted["$target"]=backup
done
mountedbk=()
while read -r _ mpnt _;do
printf -v mpnt %b $mpnt
if [[ -v tobemounted["$mpnt"] ]] ;then
[[ ${tobemounted["$mpnt"]} == backup ]] && mountedbk+=("$mpnt")
unset tobemounted["$mpnt"]
fi
done </proc/self/mounts
[[ -v tobemounted["$source"] ]] && die "Source '$source' not mounted."
for missing in "${!tobemounted[#]}";do
warn "Backup '$missing' not mounted."
done
(( ${#mountedbk[#]} == 0 )) && die "No backup present."
if (( ${#mountedbk[#]} < ${#backups[#]} ));then
printf -v msg 'Only %d / %d backups targets are present.' \
${#mountedbk[#]} ${#backups[#]}
warn "$msg"
case $* in
*-n* ) die Exit now. ;;
*-y* ) warn Process anyway. ;;
* ) read -rsn 1 -p 'Process anyway? (N/y): ' ans
[[ "${ans//[yY]}" ]] && { echo No; die 'User abort.';}
echo Yes ;;
esac
fi
pids=() resultmsg=(success)
for target in "${mountedbk[#]}";do
echo rsync "$source/." "$target/." &
pids[$!]="$target"
done
while ((${#pids[#]}));do
wait -np pid
printf 'backup to "%s" end with %s.\n' \
"${pids[pid]}" "${resultmsg[$?]:-error}"
unset 'pids[pid]'
done
Replace line:
echo rsync "$source/." "$target/." &
by somethine like:
rsync <your rsync options> "$source/SourcePath/." "$target/TargetPath/." &
Outputs could look like:
When source not ready:
$ ./backups.sh
./backups.sh[3721068] ERROR: Source '/media/card' not mounted.
When none of the targets are ready.
$ ./backups.sh
./backups.sh[3729071] WARNING: Backup '/media/backup' not mounted.
./backups.sh[3729071] WARNING: Backup '/media/backup2' not mounted.
./backups.sh[3729071] WARNING: Backup '/media/other backup' not mounted.
./backups.sh[3729071] ERROR: No backup present.
When some but not all targets are ready.
$ ./backups.sh
./backups.sh[3725569] WARNING: Backup '/media/backup2' not mounted.
./backups.sh[3725569] WARNING: Backup '/media/other backup' not mounted.
./backups.sh[3725569] WARNING: Only 1 / 3 backups targets are present.
Process anyway? (N/y): Yes
backup to "/media/backup" end with success.
$ ./backups.sh
./backups.sh[3729355] WARNING: Backup '/media/backup' not mounted.
./backups.sh[3729355] WARNING: Backup '/media/other backup' not mounted.
./backups.sh[3729355] WARNING: Only 1 / 3 backups targets are present.
Process anyway? (N/y): Yes
backup to "/media/backup2" end with success.
$ ./backups.sh
./backups.sh[3854708] WARNING: Backup '/media/backup2' not mounted.
./backups.sh[3854708] WARNING: Backup '/media/other backup' not mounted.
./backups.sh[3854708] WARNING: Only 1 / 3 backups targets are present.
Process anyway? (N/y): No
./backups.sh[3854708] ERROR: User abort.
$ ./backups.sh -y
./backups.sh[3734321] WARNING: Backup '/media/backup' not mounted.
./backups.sh[3734321] WARNING: Backup '/media/other backup' not mounted.
./backups.sh[3734321] WARNING: Only 1 / 3 backups targets are present.
./backups.sh[3734321] Process anyway.
backup to "/media/backup2" end with success.
$ ./backups.sh -n
./backups.sh[3854855] WARNING: Backup '/media/backup2' not mounted.
./backups.sh[3854855] WARNING: Backup '/media/other backup' not mounted.
./backups.sh[3854855] WARNING: Only 1 / 3 backups targets are present.
./backups.sh[3854855] ERROR: Exit now.
When all targets are ready.
$ ./backups.sh
backup to "/media/backup" end with success.
backup to "/media/backup2" end with success.
backup to "/media/other\ backup" end with success.

Related

Is there a way that you can use the "if" branch to check whether a USB stick is connected?

Is there a way like that you can use the "if" branch to check whether a USB-Stick is connected? And if the stick is not connected, a message should then be issued.
Something like:
if [-e /sdb1]
then
cp /home/backup/* /sdb1
rm -r /home/backup/*
echo "your files are successfully transferred."
else
echo "please be sure, if your USB-Stick is connected"
fi
No, the mount point exists whether or not a USB device is connected.
Something like this should suffice:
if [[ $(df | grep "/sdb1") && -d /sdb1 && -w /sdb1 ]]
That is if, of course, you have actually created the directory /sdb1/.
Use the findmnt utility (installed by default on RH and Ubuntu, at least)
findmnt /backup
echo $?
1
Return of 1 means not mounted.
Here is some sample code using it with an if statment
findmnt /backup >/dev/null
if [ $? = 0 ]; then
echo "It's mounted all right"
else
r=$(( RANDOM % 4 ))
echo "USB is not mounted."
case $r in
0) echo "Check the couch cushions."
;;
1) echo "I think I saw it in the kitchen."
;;
2) echo "Sign up for Prime and get free shipping!"
;;
3) echo "The dog ate it."
;;
esac
fi

Use of echo >> produces inconsistent results

I've been trying to understand a problem that's cropped up with some of the scripts we use at work.
To generate many of our script logs, we utilize the exec command and file redirects to print all output from the script to both the terminal and a log file. Occasionally, for information that doesn't need to be displayed to the user, we do a straight redirect to the log file.
The issue we're seeing occurs on the last line of output to the file when we're printing the number of errors that occurred during that execution: The text doesn't get printed to the file.
In an attempt to diagnose the problem, I wrote a simplified version of our production script (script1.bash) and a test script (script2.bash) to try to tease out the problem.
script1.bash
#!/bin/bash
log_name="${USER}_`date +"%Y%m%d-%H%M%S"`_${HOST}_${1}.log"
log="/tmp/${log_name}"
log_tmp="/tmp/temp_logs"
err_count=0
finish()
{
local ecode=0
if [ $# -eq 1 ]; then
ecode=${1}
fi
# This is the problem line
echo "Error Count: ${err_count}" >> "${log}"
mvlog
local success=$?
exec 1>&3 2>&4
if [ ${success} -ne 0 ]; then
echo ""
echo "WARNING: Failed to save log file to ${log_tmp}"
echo ""
ecode=$((ecode+1))
fi
exit ${ecode}
}
mvlog()
{
local ecode=1
if [ ! -d "${log_tmp}" ]; then
mkdir -p "${log_tmp}"
chmod 775 "${log_tmp}"
fi
if [ -d "${log_tmp}" ]; then
rsync -pt --bwlimit=4096 "${log}" "${log_tmp}/${log_name}" 2> /dev/null
[ $? -eq 0 ] && ecode=0
if [ ${ecode} -eq 0 ]; then
rm -f "${log}"
fi
fi
}
exec 3>&1 4>&2 >(tee "${log}") 2>&1
ecode=0
echo
echo "Some text"
echo
finish ${ecode}
script2.bash
#!/bin/bash
runs=10000
logdir="/tmp/temp_logs"
if [ -d "${logdir}" ]; then
rm -rf "${logdir}"
fi
for i in $(seq 1 ${runs}); do
echo "Conducting run #${i}/${runs}..."
${HOME}/bin/script1.bash ${i}
done
echo "Scanning logs from runs..."
total_count=`find "${logdir}" -type f -name "*.log*" | wc -l`
missing_count=`grep -L 'Error Count:' ${logdir}/*.log* | grep -c /`
echo "Number of runs performed: ${runs}"
echo "Number of log files generated: ${total_count}"
echo "Number of log files missing text: ${missing_count}"
My first test indicated roughly 1% of the time the line isn't written to the log file. I then proceeded to try several different methods of handling this line of output.
Echo and Wait
echo "Error Count: ${err_count}" >> "${log}"
wait
Alternate print method
printf "Error Count: %d\n" ${err_count} >> "${log}"
No Explicit File Redirection
echo "Error Count: ${err_count}"
Echo and Sleep
echo "Error Count: ${err_count}" >> "${log}"
sleep 0.2
Of these, #1 and #2 each had a 1% fail rate while #4 had a staggering 99% fail rate. #3 was the only methodology that had a 0% fail rate.
At this point, I'm at a loss for why this behavior is occurring, so I'm asking the gurus here for any insight.
(Note that the simple solution is to implement #3, but I want to know why this is happening.)
Without testing, this looks like a race condition between your script and tee. It's generally better to avoid multiple programs writing to the same file at the same time.
If you do insist on having multiple writers, make sure they are all in append mode, in this case by using tee -a. Appends to the local filesystem are atomic, so all writes should make it (this is not necessarily true for networked file systems).

BASH: redirect for log dillema / duplicate redirection for each loop iteration

I've got a redirect dilemma that I can't get past in a bash backup script I'm developing in CentOS 6.4. I want to redirect all output to two separate files: one tmp and one permanent. The script loops through an external source list and I'd like for the tmp log files to be specific to the source, so that I can send an email if that specific source had errors containing that log (and conversely remove the tmp if the backup completes without error).
I'm using exec to tee my output:
exec > >(tee -a ${templog} /var/log/rob/rob.log) 2>&1
This works if I place at the top of the script, but here the variable isn't defined yet, so I can't do source-specific logs.
If I place this within the while loop, it grabs the variable, but writes a copy of each line determined by the total iterations of the loop; for the example below, I have four sources it iterates through, so I get output for each source in quadruplicate:
-S-07/11/14 09:15:35 ROB-Source Process for cc2-gamma has started-S-
-S-07/11/14 09:15:35 ROB-Source Process for cc2-gamma has started-S-
-S-07/11/14 09:15:35 ROB-Source Process for cc2-gamma has started-S-
-S-07/11/14 09:15:35 ROB-Source Process for cc2-gamma has started-S-
Share cc2-gamma is not Mounted. Try 1 of 5 to mount...
Share cc2-gamma is not Mounted. Try 1 of 5 to mount...
Share cc2-gamma is not Mounted. Try 1 of 5 to mount...
Share cc2-gamma is not Mounted. Try 1 of 5 to mount...
Is there a different way to tee the output within the loop to prevent this (without touching each line of course)? Or is there something rotten in my loops that I'm not seeing? Here's the whole script. Please excuse the mess and style.. I'm clearly not finished. I didn't include the config.conf and backup source file as they don't affect the output. Let me know if needed. Thanks.
#!/bin/bash
#V.2014.0723 - Radation Oncology Backup script
#declarations
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/rob
source /rob/conf/config.conf
while read smbdir 'smbpath' exclfile drive foldername; do
#loop declarations
mountedfile=/rob/${smbdir}.MOUNTED
runningfile=/rob/${smbdir}.RUNNING
lastrunfile=/rob/${smbdir}_${foldername}.LASTRUN
templog=/rob/${smbdir}_${foldername}.TMPLOG
errorfile=/rob/${smbdir}_${foldername}.HAD_ERRORS
backupfile=/rob/${baname:0:3}_rtbackup.sql.bz2 # for the -l seccton below -- sql backup of backup.sql
#exec > >(tee -a ${templog} /var/log/rob/rob.log) 2>&1
### SOURCE BACKUP ##############################################################################################
if [ "$1" == "-s" ]
then
exec > >(tee -a /var/log/rob/rob.log ${templog}) 2>&1
#Write Source STDOUT and STDERR to both permanent and temporary log file. Must be in loop to use variables.
#exec > >(tee -a ${templog} /var/log/rob/rob.log) 2>&1
#exec > >(tee -a /var/log/rob.log ${templog}) 2>
if [ "${sources_active}" == "1" ]
then
echo "-S-$(date "+%m/%d/%y %T") ROB-Source Process for $smbdir has started-S-"
# unmount all cifs shares, due to duplicate mounts, write file to prevent concurrentcy
umount -a -t cifs > /dev/null
# The following will test to see if the souce is mounted, and if not, mount it.
for i in {1..5}
do
if mountpoint -q /mnt/${smbdir}/${drive}/${foldername}
then
echo "Share ${smbdir} is Mounted."
touch $mountedfile
break
else
sleep 2
echo "Share ${smbdir} is not Mounted. Try $i of 5 to mount..."
mkdir -p /mnt/${smbdir}/${drive}/${foldername} > /dev/null
mount -t cifs ${smbpath} -o ro,username=<USER>,password=<PW>,workgroup=<DOMAIN> /mnt/${smbdir}/${drive}/${foldername}
fi
done
# Test to see if above was successful, and if rob is not already running, run the backup.
if [[ -f ${mountedfile}&& ! -f ${runningfile} ]]
then
src="/mnt/${smbdir}/$drive"
dst="/backup/rob/"
touch ${runningfile}
/root/bin/rtbackup -m /mnt -p ${src}/${foldername} -b ${dst} -x #${exclfile}
if [ "$?" -ne "0" ]; then
#Errors Running RTBackup
rm -f ${runningfile} > /dev/null 2>&1
rm -f ${mountedfile}> /dev/null 2>&1
echo "$(date "+%m/%d/%y %T") Source Process for ${smbdir} had errors running:-SSS"
echo "$errors" >&2
touch ${errorfile}
exit 1
else
echo "What the hell is this doing?"
fi
#NO Errors Running RTBACKUP
rm -f ${templog}
rm -f ${runningfile} > /dev/null 2>&1
rm -f ${mountedfile} > /dev/null 2>&1
echo "$(date "+%m/%d/%y %T") Source Process for ${smbdir} did not have any errors"
else
#backup will *NOT* run, cleaning up and logging
rm -f ${mountedfile} > /dev/null 2>&1
echo "$(date "+%m/%d/%y %T") ${smbdir} could not be mounted, or is already in progress. Backup could not complete."
touch ${errorfile}
tail /var/log/rob/robso.log | mail -s "ROBSO Failed to run for ${smbdir} on ${baname}" ${email}
fi
echo "-F-$(date "+%m/%d/%y %T") ROB-Source Process for ${smbdir} has finished-F-"
#break
elif [[ "${sources_active}" == "0" ]]
then
echo "***$(date "+%m/%d/%y %T") ROB-Source Process for ${smbdir} did not run because the job is not set as active***"
#break
fi
done < /rob/conf/${baname}.conf
if [ $? -eq 10 ]; then exit 0; fi
You can use curly braces to redirect a set of commands; as it says in the bash manual about command grouping, "When commands are grouped, redirections may be applied to the entire command list". It behaves more-or-less like an anonymous function.
{
command1
command2
} > >(tee -a ${templog} /var/log/rob/rob.log) 2>&1
You can do the same with a named function, too, if you're so inclined, but I don't know offhand what environment would be used to expand the redirections. (If you do, please edit this answer!)
# Untested. This MIGHT work.
your_log_command() {
command1
command2
} > >(tee -a $1 /var/log/rob/rob.log) 2>&1
your_log_command $templog
your_log_command $something_else

Check if NFS Directory Mounted without Large Hangs on Failure

I'm writing a shell script to check whether or not certain nfs mounts can be seen by nodes in a cluster.
The script works by doing an ls /nfs/"machine" |wc -l and if it's greater than 0 it will pass the test. My main concern with this solution is how long ls will hang if a disk is not mounted.
I had a go at the solution in this question "bash checking directory existence hanging when NFS mount goes down" but the results did not correspond with what was actually mounted.
I also tried doing a df -h /nfs/"machine" but that has a large hang if the disk isn't mounted.
Basically, is there an alternate way which can let me know if a disk is mounted or not without large hangs?
Alternatively, is there a way of restricting the time that a command can be executed for?
Thanks in advance!
Ok I managed to solve this using the timeout command, I checked back here to see that BroSlow updated his answer with a very similar solution. Thank you BroSlow for your help.
To solve the problem, the code I used is:
if [[ `timeout 5s ls /nfs/machine |wc -l` -gt 0 ]] ; then
echo "can see machine"
else
echo "cannot see machine"
fi
I then reduced this to a single line command so that it could be run through ssh and put inside of a loop (to loop through hosts and execute this command).
Couple of possibilities:
1)
find /nfs/machine -maxdepth 0 -empty should be a lot faster than ls /nfs/machine, though I'm not sure that's the problem in this case (also not sure sleep is needed, but might be some offset.
if [[ $(find /nfs/machine -maxdepth 0 -empty 2> /dev/null) == "" ]]; then
sleep 1 && [[ $(mount) == *"/nfs/machine"* ]] && echo "mounted" || echo "not mounted"
else
echo "mounted"
fi
2)
timeout 10 ls -A /nfs/machine | wc -l
if [[ $? > 0 ]]; then
echo "mounted"
else
echo "not mounted"
fi

Linux Shell Script: How to detect NFS Mount-point (or the Server) is dead?

Generally on NFS Client, how to detect the Mounted-Point is no more available or DEAD from Server-end, by using the Bash Shell Script?
Normally i do:
if ls '/var/data' 2>&1 | grep 'Stale file handle';
then
echo "failing";
else
echo "ok";
fi
But the problem is, when especially the NFS Server is totally dead or stopped, even the, ls command, into that directory, at Client-side is hanged or died. Means, the script above is no more usable.
Is there any way to detect this again please?
"stat" command is a somewhat cleaner way:
statresult=`stat /my/mountpoint 2>&1 | grep -i "stale"`
if [ "${statresult}" != "" ]; then
#result not empty: mountpoint is stale; remove it
umount -f /my/mountpoint
fi
Additionally, you can use rpcinfo to detect whether the remote nfs share is available:
rpcinfo -t remote.system.net nfs > /dev/null 2>&1
if [ $? -eq 0 ]; then
echo Remote NFS share available.
fi
Added 2013-07-15T14:31:18-05:00:
I looked into this further as I am also working on a script that needs to recognize stale mountpoints. Inspired by one of the replies to "Is there a good way to detect a stale NFS mount", I think the following may be the most reliable way to check for staleness of a specific mountpoint in bash:
read -t1 < <(stat -t "/my/mountpoint")
if [ $? -eq 1 ]; then
echo NFS mount stale. Removing...
umount -f -l /my/mountpoint
fi
"read -t1" construct reliably times out the subshell if stat command hangs for some reason.
Added 2013-07-17T12:03:23-05:00:
Although read -t1 < <(stat -t "/my/mountpoint") works, there doesn't seem to be a way to mute its error output when the mountpoint is stale. Adding > /dev/null 2>&1 either within the subshell, or in the end of the command line breaks it. Using a simple test: if [ -d /path/to/mountpoint ] ; then ... fi also works, and may preferable in scripts. After much testing it is what I ended up using.
Added 2013-07-19T13:51:27-05:00:
A reply to my question "How can I use read timeouts with stat?" provided additional detail about muting the output of stat (or rpcinfo) when the target is not available and the command hangs for a few minutes before it would time out on its own. While [ -d /some/mountpoint ] can be used to detect a stale mountpoint, there is no similar alternative for rpcinfo, and hence use of read -t1 redirection is the best option. The output from the subshell can be muted with 2>&-. Here is an example from CodeMonkey's response:
mountpoint="/my/mountpoint"
read -t1 < <(stat -t "$mountpoint" 2>&-)
if [[ -n "$REPLY" ]]; then
echo "NFS mount stale. Removing..."
umount -f -l "$mountpoint"
fi
Perhaps now this question is fully answered :).
The final answers give by Ville and CodeMonkey are almost correct. I'm not sure how no one noticed this, but a $REPLY string having content is a success, not a failure. Thus, an empty $REPLY string means the mount is stale. Thus, the conditional should use -z, not -n:
mountpoint="/my/mountpoint"
read -t1 < <(stat -t "$mountpoint" 2>&-)
if [ -z "$REPLY" ] ; then
echo "NFS mount stale. Removing..."
umount -f -l "$mountpoint"
fi
I have ran this multiple times with a valid and invalid mount point and it works. The -n check gave me reverse results, echoing the mount was stale when it was absolutely valid.
Also, the double bracket isn't necessary for a simple string check.
Building off the answers here, I found some issues in testing that would output bad data due to how the $REPLY var would get updated (or not, if the result was empty), and the inconsistency of the stat command as provided in the answers.
This uses the stat command to check the FS type which responds to changes pretty fast or instant, and checks the contents of $REPLY to make sure the fs is NFS [ ref: https://unix.stackexchange.com/questions/20523/how-to-determine-what-filesystem-a-directory-exists-on ]
read -t1 < <(timeout 1 stat -f -c %T "/mnt/nfsshare/" 2>&-);if [[ ! "${REPLY}" =~ "nfs" ]];then echo "NFS mount NOT WORKING...";fi

Resources