Skip to content

Instantly share code, notes, and snippets.

@ggets
Created February 14, 2021 15:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ggets/4286e24b071b34b53f4c1bdd52100a70 to your computer and use it in GitHub Desktop.
Save ggets/4286e24b071b34b53f4c1bdd52100a70 to your computer and use it in GitHub Desktop.
Read TBW script for Samsung 860 Evo (and Pro) drives for Windows and Linux
#!/bin/bash
################################################################################
### TBW for Samsung SSD ###
### -by GGetsov ###
### ------------------------------------------------------------------------ ###
### Requires: Cygwin, bash (with arrays support), bc, smartctl ###
################################################################################
arrtest[0]="1"||(echo "Failure: arrays not supported in this version of bash."&&exit 2)
#var
ver="0.2.4"
rev="14.02.2021"
unset b
declare -A b #binary tools
unset p
declare -A p #parameters
unset d
declare -A d #smart data
unset a
declare -A a #smart attributes
unset t
declare -A t #tbw life
unset e
declare -a e #errors
#fn
e(){
${b[echo]} -e "${@}"
}
err(){
e "${1:-Insufficient arguments. Type \"${0} --help\" to see usage help.}"
}
b(){
[ -z ${1+x} ]&&err "No commands to autodetect!"&&exit 1
while [[ "${#}" > "0" ]];do
t="$(type -t ${1})"
if [[ "${t}" == "builtin" ]]||[[ "${t}" == "function" ]]||[[ "${t}" == "alias" ]];then
b[${1}]="${1}"
elif [[ "${t}" == "file" ]];then
b[${1}]="$(type -p ${1})"
else
err "Could not find the command: ${1}"&&exit 1
fi
shift
done
}
human_size(){
[ -z ${1+x} ]&&err "No integer passed to convert size!"&&exit 1
declare -a c
c[0]="B"
c[1]="KB"
c[2]="MB"
c[3]="GB"
c[4]="TB"
c[5]="PB"
declare -i i=0
cap="${1}"
r="${cap} ${c[${i}]}"
while [[ "${i}" < "5" ]]&&[[ "$(${b[bc]}<<<"${cap}>1024")" == 1 ]];do
i+=1
cap="$(${b[bc]}<<<"scale=3;${cap}/1024")"
r="${cap} ${c[${i}]}"
done
echo "${r}"
}
#tools
b type echo printf bc
case "${OSTYPE}" in
*"msys"*|*"win"*)
b[smartctl]="/computer/c/bin/smartmontools/bin/smartctl"
defdrive="hda" # on windows default to 'hda'
;;
*"linux"*|*"android"*|*"bsd"*)
b[smartctl]="/usr/sbin/smartctl"
defdrive="sda" # on unix-based default to 'sda'
;;
*)
b[smartctl]="echo"
defdrive="sda" # on all other default to 'sda'
;;
esac
#arg
while [[ "${#}" > "0" ]];do
case "${1}" in
--help)
e "TBW for Samsung SSD script by hkr v${ver} (rev${rev})"
e " Usage:"
e " -d, --drive: Drive (Optional. Default: 'hda')"
e ""
e " --test: Only test print the string, don't execute"
e ""
exit 0
;;
-d|--drive)
[ -z ${2+x} ]||[[ ${2} == -* ]]&&err&&exit 1
p[drive]="${2}"
shift
;;
--test)
p[test]="1"
;;
*)
;;
esac
shift
done
#main
p[drive]="${p[drive]:=${defdrive}}" # default drive value is os-dependent
# these are valid for 860 EVO only.
t[250]="$[150*1024]"
t[500]="$[300*1024]"
t[1000]="$[600*1024]"
t[2000]="$[1200*1024]"
t[4000]="$[2400*1024]"
# these are valid for 860 EVO PRO only.
# tpro[256]="$[300*1024]"
# tpro[512]="$[600*1024]"
# tpro[1024]="$[1200*1024]"
# tpro[2048]="$[2400*1024]"
# tpro[4096]="$[4800*1024]"
[ -z ${p[test]+x} ]||{
e "${b[smartctl]} -i -A ${p[drive]}"
exit 0
}
d[data]="$(${b[smartctl]} -i -A "${p[drive]}")"
[[ "${?}" > "0" ]]&&{
e "something went wrong."
exit 1
}
d[info]="${d[data]//=== START OF READ SMART DATA SECTION ===*}"
d[info]="${d[info]//*=== START OF INFORMATION SECTION ===}"
d[info]="${d[info]//[$'\r']}"
d[info]="${d[info]#"${d[info]%%[!$'\t\n ']*}"}"
d[info]="${d[info]%"${d[info]##*[!$'\t\n ']}"}"
IFS=$'\n'
al=(${d[info]})
unset IFS
for i in "${!al[@]}";do
k="${al[${i}]%%:[$'\t ']*}"
k="${k%%[$'\t ']is}"
v="${al[${i}]#*:}"
v="${v#"${v%%[!$'\t\n ']*}"}"
v="${v%"${v##*[!$'\t\n ']}"}"
a[${k}]="${v}"
done
d[attr]="${d[data]//*=== START OF READ SMART DATA SECTION ===}"
d[attr]="${d[attr]//*RAW_VALUE}"
d[attr]="${d[attr]//[$'\r']}"
d[attr]="${d[attr]#"${d[attr]%%[!$'\t\n ']*}"}"
d[attr]="${d[attr]%"${d[attr]##*[!$'\t\n ']}"}"
IFS=$'\n'
al=(${d[attr]})
unset IFS
for i in "${!al[@]}";do
arr=(${al[${i}]#"${al[${i}]%%[!$'\t\n ']*}"})
a[${arr[1]}]="${arr[-1]}"
done
[ -z ${a[Total_LBAs_Written]+x} ]&&{
e "This does not seem to be an SSD!"
exit 2
}
tbw="${a[Total_LBAs_Written]}"
# echo "${tbw}"
tbw="$[${tbw:=0}*512]"
# echo "${tbw}"
# Old method to get direct "user capacity" value
# doesn't work after over-provisioning since
# visible capacity is reduced.
# Our best chance is to find the closest value:
cap="${a[User Capacity]//,}"
cap="${cap%[$'\t ']bytes*}"
comp="${cap}"
declare -i num=0
declare -i test=-1
declare -i best=-1
for i in "${!t[@]}"; do
numt="${i}"
num="${i}"
((num=(num*1024*1024*1024)))
[[ "${num}" > "${comp}" ]]&&{
((cur=(num-comp)))
[[ "${best}" == "-1" ]]||[[ "${cur}" < "${test}" ]]&&{
test="${cur}"
best="${num}"
realcap="${i}"
}
}
done
l="$[${t[${realcap}]}*1024*1024]"
wf="$(${b[bc]}<<<"scale=3;${tbw}/1024/${l}")"
# echo "wf:${wf}" # wear factor
df="${wf}"
[ "$(${b[bc]}<<<"${df}>1")" -eq 1 ]&&df="1"
p="$(${b[bc]}<<<"scale=1;(${wf}*100)/1")"
[ "$(${b[bc]}<<<"${p}<1")" -eq 1 ]&&[ "$(${b[bc]}<<<"${p}>0")" -eq 1 ]&&p="0${p}"
p=" ${p}% "
bl="60"
fw="$(${b[bc]}<<<"scale=0;(${bl}*${df}/1)")"
blr="$(${b[printf]} "%${fw}s")"
blf="${blr// /|}"
blr="$(${b[printf]} "%$[${bl}-${fw}]s")"
blb="${blr// /-}"
blr="${blf}${blb}"
bl="$[(${bl}/2)-(${#p}/2)]"
blb="${blr:$[${#blr}-${bl}]}"
[ "$((${#p}%2))" -ne "0" ]&&bl="$[${bl}-1]"
blf="${blr:0:${bl}}"
blr="${blf}${p}${blb}"
e "Drive: ${p[drive]}"
e "Device: ${a[Device Model]}"
e "Life ( $(${b[bc]}<<<"scale=2;(${tbw}/1024/1024/1024/1024)")/$[${l}/1024/1024/1024] TB ):\n[${blr}]"
dfc="$(human_size $((${realcap}*1024*1024*1024)))" # drive full capacity
vis="$(human_size ${cap})" # visible capacity
opc="$(human_size $(((${realcap}*1024*1024*1024)-${cap})))" # over-provisioning capacity
e "Full capacity: ${dfc}"
e "Visible capacity: ${vis}"
e "OP capacity: ${opc}"
e "TBW (round): $(human_size ${tbw})"
e "TBW (in GB): $[${tbw}/1024/1024/1024] GB"
e "Temp: ${a[Airflow_Temperature_Cel]} C°"
e "Hours: ${a[Power_On_Hours]}"
e "Cycles: ${a[Power_Cycle_Count]}"
e "Firmware: ${a[Firmware Version]}"
e+=(Reallocated_Sector_Ct)
e+=(Runtime_Bad_Block)
e+=(Program_Fail_Cnt_Total)
e+=(Erase_Fail_Count_Total)
e+=(Reported_Uncorrect)
e+=(Uncorrectable_Error_Cnt)
e "Errors: "
# for i in "${!a[@]}";do e " ${a[${i}]}";done
for i in "${!e[@]}";do
e -n " "
[ -z "${a[${e[${i}]}]+x}" ]&&{
e -n "-"
}||{
e -n "${a[${e[${i}]}]}"
}
e " (${e[${i}]})"
done
exit 0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment