
Spinning down external usb hard drives in Ubuntu doesn’t allways happen automatically in Ubuntu. To force a spindown, you can issue the command
sync sdparm --flexible --command=stop /dev/sdX &>/dev/null
where you should replace the sdX entry by the correct name of your external hard drive (usually sdb, sdc or similar).
To do this automatically every 30 minutes, save the code below as e.g. ~/bin/spindown/spindown.sh.
# !/bin/sh # Get new state from diskstats NEWstate=$(cat /proc/diskstats | grep $1) echo $NEWstate > NEWstate.txt # compare md5 sums md5new=$(md5sum NEWstate.txt | sed 's/ .*//') md5old=$(md5sum OLDstate.txt | sed 's/ .*//') # if no changes, power down if [ "$md5new" = "$md5old" ]; then sdparm --flexible --command=stop /dev/$1 &>/dev/null fi # Write current state to file echo $NEWstate > OLDstate.txt
Then, add the entry
*/30 * * * * root sh /home/user/bin/spindown/spindown.sh sdX
to the cron file /etc/crontab. The script automatically checks to see if the drive has been active for the last 5 mins. If not, it forces a spindown.

Hey, that’s what I have searched for a long time. Thanks, man. It works!
Great post – works really well. Thanks.
If anybody’s interested, I also added some code at the top of the script to check that it is being run as root…
if [ "$(id -u)" != "0" ]; then
echo “This script must be run as root” 1>&2
exit 1
fi
Obviously this is not needed for cron, but useful for testing etc.
hey!
many thanks for the script.
for external usb drive compatibility i added the following lines.
#convert UUID to ID
disk=$(blkid | grep $1 | cut -c 6-9)
#check if the disk uuid, or pieces of it were ok
if [ ! -n $disk ]; then
echo “no such disk – disk!”
exit 1
fi
cheers
Are md5sum and sed really necessary? Could you not just do a diff between OLDstate.txt and NEWstate.txt?
[ -z $(diff OLDstate.txt NEWstate.txt) ] && sdparm –flexible –command=stop /dev/$1
Translation: Spin down when diff returns a zero-length string on comparing OLDstate.txt and NEWstate.txt. This gets rid of a few lines of code and reduces external dependencies.
Awesome, just what I was looking for since my internal SATA HD’s would not spin down anymore after upgrade to ubuntu 10.04
*/5 * * * * root sh /home/user/bin/spindown/spindown.sh sdX
should the sdX be sda, sdb, sdc etc or should it stay sdX
thanks
When i run this command, the drive stops for a second and immediately spins back up. any ideas?
# !/bin/sh
if [[ -e /dev/$1 ]]; then
echo $1 Spin power down
notify-send –icon=’/usr/share/icons/GartoonRedux/scalable/apps/gaim.svg’ “Spinning Powerdown: $1″
else
# Get new state from diskstats
NEWstate=$(cat /proc/diskstats | grep $1)
echo $NEWstate > /var/ds.state/NEWstate.$1
# compare md5 sums
md5new=$(md5sum /var/ds.state/NEWstate.$1 | sed ‘s/ .*//’)
md5old=$(md5sum /var/ds.state/OLDstate.$1 | sed ‘s/ .*//’)
# if no changes, power down
if [ "$md5new" = "$md5old" ]; then
sdparm –flexible –command=stop /dev/$1 &>/dev/null
fi
# Write current state to file
echo $NEWstate > /var/ds.state/OLDstate.$1
echo $1 Device Does Not Exist !
exit 0
fi
So… i put a notify in the script and the oldstat,txt is replaced with oldstate.$ so will permit to use it on more than one disk as the extension will be .sdX and made a sort of check if the disk exist, was a good idea and a good job to start wirh, thanks
# !/bin/sh
if [[ -e /dev/$1 ]]; then
echo $1 Spin power down
notify-send –icon=’/usr/share/icons/GartoonRedux/scalable/apps/gaim.svg’ “Spinning Powerdown: $1″
# Get new state from diskstats
NEWstate=$(cat /proc/diskstats | grep $1)
echo $NEWstate > /var/ds.state/NEWstate.$1
# compare md5 sums
md5new=$(md5sum /var/ds.state/NEWstate.$1 | sed ‘s/ .*//’)
md5old=$(md5sum /var/ds.state/OLDstate.$1 | sed ‘s/ .*//’)
# if no changes, power down
if [ "$md5new" = "$md5old" ]; then
sdparm –flexible –command=stop /dev/$1 &>/dev/null
fi
# Write current state to file
echo $NEWstate > /var/ds.state/OLDstate.$1
else
echo $1 Device Does Not Exist !
exit 0
fi
ops i posted the wrong one… this one works
@Rena
Thank you, Rena.
@whackedout
sdX should be sda
@thinmintaddict
It sounds as if a process is accessing the drive while your are trying to spin it down.
You can use ”fuser -m /path/to/device” to get a list of processes accessing the device,
and ”fuser -km /path/to/device” to kill the processes. Check out fuser’s manpage for more options.
yes sdx could be sda sdb sde or so on, seems working fine here,but i found something similar:
#!/bin/bash
let a=0
for i in `seq 0 100`
do
let a=`cat /proc/diskstats | grep “0 $1″ | awk ‘{print $(NF-2)}’`+a
sleep 0.1s
done
echo $a
if [ $a == 0 ]
then
sdparm -C stop /dev/$1
fi
exit 0
seems working also, and no need to write down the stats….
Even if i can say that the disk is almost powerd on if you even access to the desktop, i dont see much usefull this can be, the drive goes on off too many times and this is harmfull for it.
i just finished my netgear script today, nice stuff, keepd up the line at the max speed connection than can get on that moment and changes speed if the s/n or attenuation changes on the line to not break down,it’s a sort of stay on the max speed the line can support on that moment.
running that for a few days now, and i believe who is far from the box it’s a need.
@Rena
If you run this every 0.1 seconds, you seriously risk damaging the hardware. My script does it once every 30 minutes.
you can always set 10 instead of 0.1…
@admin
This script is nice ’cause it makes sure that there is no activity within a 10 second period. Run it periodically as a cron job.
What would I need to do to spin two drives down?
Cheers
Sorbus
Instead of /dev/sdX I’m using /dev/disk/by-uuid/$1 and address the drive by the uuid. That way it’s very easy to address a particular drive, as these a symlinks to the propper /dev/sdX. Udev does normally not guarantee that a certain drive is always mapped to the same /dev/sdX file but the uuid symlink does.
Cheers
Amie
@Sorbus
put one line for each drive to the crontab; note my uuid hint to address a particular drive propperly.
I added some checks and logging to the script…
Now the spindown isn’t issued as long as the drive hasn’t spin up again.
# !/bin/sh
TMPF1=”/tmp/.hddspindwn-$1-1″
TMPF2=”/tmp/.hddspindwn-$1-2″
FLAG=”/tmp/.hddspindwnflg-$1″
LOG_FILE=/var/log/hddspindown.log
STAMP=`date +”%d.%m.%g %H:%M:%S”`
[ ".$1" != "." ] && disks=$(ls /dev/sd* | grep $1)
if [ ".$disks" = "." ]; then
echo “Please enter a valid disk device” 1>&2
exit 1;
fi
if [ "$(id -u)" != "0" ]; then
echo “This script must be run as root” 1>&2
exit 1
fi
# Get new state from diskstats
nstats=$(cat /proc/diskstats | grep $1)
echo $nstats > $TMPF1
# compare md5 sums
nmd5=”???”
omd5=”???”
[ -f $TMPF1 ] && nmd5=$(md5sum $TMPF1 | sed ‘s/ .*//’)
[ -f $TMPF2 ] && omd5=$(md5sum $TMPF2 | sed ‘s/ .*//’)
echo “$STAMP : Current MD5 : $nmd5″ >> $LOG_FILE
echo “$STAMP : Old MD5 : $omd5″ >> $LOG_FILE
# if no changes, power down
if [ "$nmd5" = "$omd5" ]; then
if [ ! -f $FLAG ]; then
touch $FLAG
hdparm -Y /dev/$1 &>/dev/null
echo “$STAMP : Spindown of /dev/$1 intiated…” >> $LOG_FILE
else
echo “$STAMP : /dev/$1 was already set to standby.” >> $LOG_FILE
fi
else
if [ -f $FLAG ]; then
rm $FLAG
echo “$STAMP : $FLAG removed because of disk-io.” >> $LOG_FILE
fi
fi
# Write current state to file
echo $nstats > $TMPF2
For a pata disk in an usb-enclosure the sdparm command seems not to work, nor does hdparm. Any suggestions highly appreciated!
# sdparm -f -C stop /dev/mapper/udisks-luks-uuid-6872fba4-c251-4efc-b3ff-0d265fdh6c42-uid1000
unable to access /dev/mapper/udisks-luks-uuid-6872fba4-c251-4efc-b3ff-0d265fdh6c42-uid1000, ATA disk?
# hdparm -Y /dev/udisks-luks-uuid-6872fba4-c251-4efc-b3ff-0d265fdh6c42-uid1000
issuing sleep command
HDIO_DRIVE_CMD(sleep) failed: Inappropriate ioctl for device
The disk is a pata 500GB WD drive.
You help me a lot, because hdparm won’t work on my harddisk.
I enhanced your script, to make log files.
I use this on my server, made with desktop harddisks, that I don’t want to spin all the time.
This script is written in bash instead of sh.
The Cron intervall is the refresh rate of the logs
#!/bin/bash
#original from http://hartvig.de
#modified By Denis B
#soxnirvana@gmail.com
#
#
#
#
#must be run as root
if [ "$(id -u)" != "0" ]; then
echo “This script must be run as root” 1>&2
exit 1
fi
#————————
# disc must be specified
if [ ! $2 ]; then
echo -e “\ntwo parameters needed:\n\n1)a disk must be specified : eg: sdb1, sdc3\n2)spindown time (since last access to disk)(Minutes)\n——————\nlog refresh time will depend to this script execution frequency\nspindown is \”2) + script_frequency\” maximum\n”
exit 1
fi
#————————
TMPDir=/tmp/hddspindwn
TMPFold=$TMPDir/$1-1
TMPFnew=$TMPDir/$1-2
FLAG_OFF=$TMPDir/$1-hddspindwnflg-OFF
FLAG_ON=$TMPDir/$1-hddspindwnflg-ON
LOG_FILE=/var/log/hddspindown-$1.log
#———————-
#time
day=$(date +%Y/%m/%d)
hour=$(date +%X)
seconds_alltime=$(date +%s)
#———————-
# Get new state from diskstats
NEWstate=$(cat /proc/diskstats | grep $1)
#create dir if doesnt exists
if [ ! -e $TMPFnew ];then mkdir $TMPDir/ -p; touch $LOG_FILE;touch $TMPFnew;fi
#get newstate file contents
echo $NEWstate > $TMPFnew
#exit if oldstate doesnt exists, but creates it
if [ ! -e $TMPFold ];then
touch $TMPFold;echo $NEWstate > $TMPFold;echo -e “\nfirst execution:\nlaunch again to compare\n”;exit 1;fi
# compare md5 sums
md5new=$(md5sum $TMPFnew | sed ‘s/ .*//’)
md5old=$(md5sum $TMPFold | sed ‘s/ .*//’)
#——————–
# if no changes
if [ "$md5new" = "$md5old" ]; then
let “last_modified = ( $seconds_alltime – $(stat -c %Y $TMPFold)) / 60 ”
#———————
#enter spindown, if its the first time the device needs a power off
if [ "$last_modified" -ge "$2" ] && [ ! -f $FLAG_OFF ] ;then
touch $FLAG_OFF
if [ -f $FLAG_ON ]; then rm $FLAG_ON; fi
echo “$day;$hour;Power OFF” >> $LOG_FILE
echo “POWER OFF”
sdparm –flexible –command=stop /dev/$1 &>/dev/null
fi
echo -e “last activity detected : $last_modified (Min ago)”
else
#———————
#activity on disk
if [ -f $FLAG_OFF ]; then rm $FLAG_OFF;fi
#if its the first time, then log power ON
if [ ! -f $FLAG_ON ]; then touch $FLAG_ON;echo “$day;$hour;Power ON” >> $LOG_FILE;echo “POWER ON”;fi
#writing the current state in a new file
echo $NEWstate > $TMPFold
fi
#———————-
“”