Created
February 14, 2021 15:00
-
-
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
This file contains 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 | |
################################################################################ | |
### 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