Как стать автором
Обновить

Правильное отключение питания SATA дисков в Ubuntu


По роду деятельности мне часто приходится подключать жёсткие диски к своему компьютеру через SATA разъём. После некоторых манипуляций с ними я обычно просто выдёргиваю питание и шлейф из разъёмов. Но каждый раз при этом диск в моих руках немного витает в воздухе. А шпиндель-то в это время ещё не остановился — что может привести к физическим повреждениям диска и потере данных.
И каждый раз я думал что вот-вот сяду и напишу скрипт для отключения питания, а потом уже буду отключать винт физически.
И вот, наконец, у меня появилось достаточно времени чтобы сесть и спокойно этим заняться.

Google честно признался, что проще всего это сделать отправив единичку в «/sys/block/sdx/device/delete». Когда же подключено несколько дисков, неудобно запоминать букву каждого и менять её в строке. А какой диск мне нужно отключить проще опознать по его названию, размеру и разделам на нём. И вот, после нескольких весёлых часов отладки и разных улучшений, у меня получился маленький скрипт:
sataStop.sh
#!/bin/bash

# - - - - - - - - - - - - - - = = = = = = = - - - - - - - - - - - - - - 
	Name="SATA Stop"
#	Desc: Turns off SATA drives
#	Usage:	sudo sh ./sataStop.sh
#
#	Author:		Samolysov Maxim
#	E-mail:		zBestR@bk.ru
#	License:	Free for all with NO guarantee. Just think & make sure you know what R`You doing
# - - - - - - - - - - - - - - = = = = = = = - - - - - - - - - - - - - - 
#	HISTORY
	Version="01.00.01";	Date=[21.04.2013]
#	+ basic functionality
# 	+ colors
#	+ once I`ve stopped ROOT disk... (just for sure it works)... So, now here is a check to not let this happens again )))
#
	Version="01.00.02";	Date=[24.04.2013]
#	! fixed bug with last disk skipped (not turned off)
	Version="01.00.03";	Date=[07.05.2013]
#	+ colorized X-letter in sdX
	Version="01.00.04";	Date=[10.05.2013]
#	+ getSize function
#	+ show disk size in GB & GiB
# in future...	Version="01.00.0";	Date=[..2013]
#	+ try to unmount mounted partitions (use with care)
#	HISTORY END
# - - - - - - - - - - - - - - = = = = = = = - - - - - - - - - - - - - - 
###	GLOBAL VARIABLES
	disks=$(ls /sys/block/ | grep sd)
	count=0
	ROOT_DISK=""
	HAS_MOUNTS=""
	SIZE=""
	# Regular Colors
	      Black='\e[01;30m' # Black
	        Red='\e[00;31m' # Red
	   LightRed='\e[01;31m' # LightRed
	      Green='\e[00;32m' # Green
	 LightGreen='\e[01;32m' # LightGreen
	     Yellow='\e[00;33m' # Yellow
	LightYellow='\e[01;33m' # LightYellow
	       Blue='\e[00;34m' # Blue
	  LightBlue='\e[01;34m' # LightBlue
	     Purple='\e[00;35m' # Purple
	LightPurple='\e[01;35m' # LightPurple
	       Cyan='\e[00;36m' # Cyan
	      White='\e[01;37m' # White
	Color_Off='\e[0m'        # Reset color
	fullName=" $LightYellow$Name$Yellow v.$LightBlue$Version $Yellow$Green$Date$Yellow"
###	GLOBAL VARIABLES END
### - - - - - - - - - - - - - - = = = = = = = - - - - - - - - - - - - - - = = = = = = = - - - - - - - - - - - - - -
#####	FUNCTIONS

checkMounted () {
mountedTo=$(sudo lsblk -ro name,mountpoint $1 | grep $1 | awk '{print $2}')
} 		# checkMounted END


getModel () {
model=$(sudo hdparm -i /dev/$1 | grep -i model| sed -e 's/Model=/\"/' -e 's/,.*$/\"/')
}		# getModel END


getSize () {
SIZE=""
#	local size=$(sudo lsblk -ro size /dev/$disk | awk 'NR == 2 {print ($1)}' | sed 's/,/./')
local device=$1
local bi=$2
local sp=""
local bsize=$(cat /sys/block/$device/size)
bsize=$( expr $bsize '*'  512)
if [[ $bsize -lt 1000 ]]; then
	bsize="$bsize"
	sp="B"
elif [[ $bsize -lt 1000000 ]]; then
	[[ $bi = 1 ]] && bsize="$bsize/1024" sp="KiB" || bsize="$bsize/1000" sp="KB"
#	sp="KB"
elif [[ $bsize -lt 1000000000 ]]; then
	[[ $bi = 1 ]] && bsize="$bsize/1048576" sp="MiB" || bsize="$bsize/1000000" sp="MB"
#	bsize="$bsize/1048576"
#	sp="MB"
else
	[[ $bi = 1 ]] && bsize="$bsize/1073741824" sp="GiB" || bsize="$bsize/1000000000" sp="GB"
#	bsize="$bsize/1073741824"
#	sp="GB"
fi
bsize=$(echo "scale=0; $bsize" | bc -l)
[[ $bsize -lt 10 ]]   && bsize=" $bsize"
[[ $bsize -lt 100 ]]  && bsize=" $bsize"
[[ $bsize -lt 1000 ]] && bsize=" $bsize"

SIZE="$bsize $sp"
} 		# getSize END


main () {
echo -e "$fullName"
# !!! Do NOT even FUCKING try to TURN OFF the ROOT disk !!! COMPUTER WILL FRIZE AND POSSIBLY LOST DATA !!!
for disk in $disks; do
	let count++
	echo -ne "$Red"
	local model=$(sudo hdparm -i /dev/$disk | grep -i model| sed -e 's/Model=/\"/' -e 's/,.*$/\"/')
	getSize "$disk" 0
	local size="$SIZE"
	getSize "$disk" 1
	local size="$size ($SIZE)"
	disk_1="sd"
	disk_2=$(echo $disk | sed 's/..//')
	echo -e "$LightYellow$count. $LightPurple$disk_1$LightYellow$disk_2\t\e[07;32m$size\e[0m\t$LightGreen$model$ColorOff"
	local parts=$(sudo lsblk -ro name,type | grep $disk | awk '/part/ {print ($1)}' )
	for part in $parts; do
		local mountedTo=$(sudo lsblk -ro name,mountpoint /dev/$part | grep $part | awk '{print $2}')
		local fileSystem=$(sudo lsblk -ro name,fstype /dev/$part | grep $part | awk '{print $2}')
		getSize "$disk/$part" 1
		fileSystem="\t$fileSystem\t $SIZE"
		if [[ $mountedTo == "" ]]; then
			mountedTo="$Green\tnot mounted $ColorOff"
		elif [[ $mountedTo == "/" ]]; then
			# !!! Do NOT even FUCKING try to TURN OFF the ROOT disk !!! COMPUTER WILL FRIZE AND POSSIBLY LOST DATA !!!
			ROOT_DISK=$count
			mountedTo="$Red\t$mountedTo$Yellow\t<--- this is$Red ROOT$ColorOff"
		elif [[ $mountedTo == "[SWAP]" ]]; then
			mountedTo="$Red\t$mountedTo$ColorOff"
		else
			HAS_MOUNTS="$HAS_MOUNTS $disk"
			mountedTo="$LightBlue\t$mountedTo$ColorOff"
		fi
		echo -e "   $Yellow$part$fileSystem$mountedTo$ColorOff"
	done
done # disk in $disks
echo -ne "$LightPurple\rEnter disk number [$LightYellow 1-$count $LightPurple] to turn off ($LightYellow \"Enter\" to exit $LightPurple):$ColorOff $LightYellow"
read answer
# !!! Do NOT even FUCKING try to TURN OFF the ROOT disk !!! COMPUTER WILL FRIZE AND POSSIBLY LOST DATA !!!
if [[ $answer == $ROOT_DISK ]]; then
	echo -e "$Red Do You$LightRed REALLY$Red believe I\`ll stop disk with $LightRed\"ROOT\"$Yellow ??? $LightGreen Sorry, Bro$Yellow...\n $LightBlue I\`m a clever script $Yellow)))$LightPurple But You can still trying$Yellow..."
sleep 15
else
	if [[ $count+1 > $answer &&  $answer > 0 ]]; then
		stop_disk=$(echo ${disks} | awk '{print $'$answer'}')
		local disk_mounted="no"
		d_isk_number=$(echo ${HAS_MOUNTS} | grep -q "${stop_disk}") && disk_mounted="yes"
		if [[ $disk_mounted == "yes" ]]; then
			echo -e "$LightPurple$stop_disk$LightBlue has mounted partitions.$Yellow Unmount them and try again."
sleep 15
		else
			local model=$(sudo hdparm -i /dev/$stop_disk | grep -i model| sed -e 's/Model=/\"/' -e 's/,.*$/\"/')
			echo -ne "Turning off $LightPurple$stop_disk $LightGreen$model...   "
			sudo sh -c "echo 1 > /sys/block/$stop_disk/device/delete"
sleep 2
			# !!! Do NOT even FUCKING try to TURN OFF the ROOT disk !!! COMPUTER WILL FRIZE AND POSSIBLY LOST DATA !!!
			ls /sys/block | grep -q "${stop_disk}" || echo -e "$LightYellow Successfully stopped."
sleep 15
		fi
	fi
fi # $answer == $ROOT_DISK
echo -e "$LightYellow Exit."
sleep 3
} 		# main END

#####	FUNCTIONS END
### - - - - - - - - - - - - - - = = = = = = = - - - - - - - - - - - - - - = = = = = = = - - - - - - - - - - - - - -
#####	START SCRIPT

clear
main
echo -ne "$ColorOff"
#####	START SCRIPT END

с удобным выводом:
image

Теперь можно смело выдёргивать шлейф из диска ведь шпинделю хватает времени остановиться (1-3 секунды) пока мои руки дотронутся до самого диска.

P.S. Если мой скрипт спасёт хоть несколько дисков и убережёт чьи-то данные от потери, я считаю задачу выполненной.
Теги:
Хабы:
Данная статья не подлежит комментированию, поскольку её автор ещё не является полноправным участником сообщества. Вы сможете связаться с автором только после того, как он получит приглашение от кого-либо из участников сообщества. До этого момента его username будет скрыт псевдонимом.