#!/bin/bash
# Fast Site Engine. Copyright © AC (acwed@ya.ru), 2004-2024. All rights reserved. 2-clause BSD license.
# https://fsen.ru/linux/md Script for Debian and based on him: KDE Neon, K/Ubuntu...
# l=/tmp/md;rm -f $l;wget -qP /tmp https://fsen.ru/inc/doc/linux/md;chmod +x $l;$l
# Для сервера: crontab -e   0 7 * * * /usr/bin/md test   каждый день в 7 утра
export LANGUAGE=en LANG=en_US.UTF-8
ERRKEY=''  # Для сервера: ключ распознавания при отсылке в скрипт pwgen 16 1
ERRSENT='' # Для сервера: отсылать файл ошибок в скрипт: https://.ru/md.php
LE='------------------------------------------------------------------------------------------------------------------------'
LN=$LE'\n'
LEN=120 # Консоль KVM от 1280x1024 px
STAGE=1
NOH=0
LS='  ' # Пробелы от начала строки 2  #  /dev/nvme0n1[p1] /dev/sda[1]   nvme0n1-disk nvme0n1p1-part
MDS=''
MDUSED=''
CRAID=''
MO=()
MOX=0
BG='\033[1;32m' # Зелёный жирный текст
BN='\033[0m' # Все атрибуты по умолчанию
DISKS=()
DISKSH='' # rtrim "$S" '[0-9]' удалить все цифры в конце
ltrim(){ local S=${1-} C=${2-[[:space:]]}; while [[ $S == $C* ]]; do S="${S##$C}"; done; echo "$S"; }
rtrim(){ local S=${1-} C=${2-[[:space:]]}; while [[ $S == *$C ]]; do S="${S%%$C}"; done; echo "$S"; }
trim(){ local S=${1-} C=${2-[[:space:]]}; echo "$(rtrim "$(ltrim "$S" "$C")" "$C")"; }
sclear(){ local S=${1-}; S="$(trim "$S")"; S=${S//    / }; S=${S//  / }; S=${S//  / }; echo "$S"; }
isint(){ # test
  local A=${1-} R='^[0-9-]+$'
  [[ $A =~ $R ]] && echo 1 || echo 0
}
int(){ # test # To int
  local A=${1-}
  A=${A//[^0-9,.-]}; A=${A//---/-}; A=${A//--/-}; A=${A//,/.}; A=${A%%.*}
  [[ -z $A || $A == '-' ]] && echo 0 || echo $((10#$A))
}
ceil(){ # test  # Округляет дробь в большую сторону
  local F=${1-0} A=0 B=0
  F=${F//[^0-9,.-]}; F=${F//,/.}
  if [[ $F =~ '.' ]]; then
    A=${F%%.*}; [[ $A == '-' ]] && A=0 || A=$((10#$A))
    B=$(int "${F#*.}")
  else A=$F; fi
  if ((B > 0)) && ! [[ $A =~ '-' ]]; then A=$((A + 1)); fi
  echo $A
}
round(){ # test
  local X=${2-2}
  echo $(echo 'scale='$X'; '$1'/1' | bc)
}
bytes(){ # test
  local B=${1-0} I=${2-0} S=0 A=('B' 'KB' 'MB' 'GB' 'TB' 'PB')
  S=$B
  while ((B >= 1024)); do
    S=$(echo 'scale=9; '$S'/1024' | bc)
    B=$((B / 1024))
    I=$((I + 1))
  done
  echo $(round $S)' '${A[$I]}
}
ex(){ # test # Разбивает строку S через делитель D возвращает элемент N
  local S=${1-} N=${2-0} D=${3- } C=${4-1} A=() B='¿from¤start¿'
  [[ $C == 1 ]] && S=$(sclear "$S"); S=${S// /§}; D=${D// /§}; A=(${S//$D/ })
  if ((${#A[*]} == 1)) && [[ "${S/#$D/$B}" =~ $B ]]; then A[1]=${A[0]}; A[0]=''; fi
  [[ -n ${A[$N]+x} ]] && echo "${A[$N]//§/ }" || echo ''
}
str_pad_repeat(){ # test
  local S=${1-} L=${2-0} R=''
  while ((${#R} < L)); do R+="$S"; done
  echo "${R:0:$L}"
}
str_pad(){ # test # input, pad_length, pad_string, pad_type[0=STR_PAD_LEFT, 1=STR_PAD_RIGHT, 2=STR_PAD_BOTH, 3=По центру добавляет только слева]
  local S=${1-} L=${2-0} D=${3- } T=${4-1} N=0 P=''
  N=$((L - ${#S}))
  if ((N > 0)); then P="$(str_pad_repeat "$D" "$N")"
    if ((T == 0)); then S="$P$S"
    elif ((T == 1)); then S+="$P"
    elif ((T > 1)); then
      ((N % 2 != 0)) && N=$(ceil "$N.9") # $((9 / 2)) = 4
      N=$((N / 2)); P="${P:0:$N}"
      ((T == 3)) && S="$P$S" || S="$P$S$P";
      S="${S:0:$L}"
    fi
  fi; echo "$S"
}
align(){ # test # Выравнивает строку по пробелам
  local S=${1-} L=${2-'17 10 24 24 24'} C=${3-} R='' T='' I=0
  S=($(sclear "$S")); L=($L)
  for ((I=0; I<${#S[*]}; I++)); do
    [[ -z ${L[$I]+x} ]] && L[$I]=30
    T="${S[$I]}"; [[ -n $C ]] && T="${T/#*=}";
    R+="$(str_pad "$T" ${L[$I]})"
    [[ ${R: -1} != ' ' ]] && R+=' '
  done; echo "$R"
}
err(){ # test
  local A=${1-} B=${2-'Ошибка!'} R='\e[41m' M='\e[5m' C='\e[0m'
  if ((NOH == 0)); then echo -e "$LS$R $M$B$C$R $A $C" # Белое на красном
  else echo -e "$LS $B $A"; fi
  # echo -e "\e[5;31;47m$B\e[0m\e[31;47m $A\e[0m" # Красное на белом
}
diskshealth(){ # test
  local K='' L='' N='' C='' A=$(lsblk -I 8 -ndpo NAME,SIZE,SERIAL,MODEL,REV) B=() S='' V='' W='' T='' R='' Z=0
  if ((NOH == 0)); then clear; fi
  echo -e "$LN$LS $(align 'NAME SIZE SERIAL MODEL REV')"
  while read L; do
    echo -e "$LN$LS $(align "$L")"
    N=$(ex "$L") # /dev/nvme0n1p1 /dev/sda
    S=" $N  Серийный номер: "$(ex "$L" 2)
    C=$(sudo smartctl -H "$N")
    while read K; do
      if [[ $K =~ 'result:' ]]; then C=$K; break; fi
    done <<< "$(echo -e "$C")"
    echo -e "\n$LS $C"
    if ! [[ $C =~ 'result: PASSED' ]]; then err "Сбой SMART диска:$S"; fi
    C=$(sudo smartctl -A "$N") # Атрибуты; -a показать всё  sudo smartctl -A /dev/sda
    while read K; do # 3-VALUE 4-WORST 5-THRESH 10-RAW_VALUE
      K=$(sclear "$K"); B=($K);
      if (($(isint "${B[0]}"))); then V=$(int "${B[3]}"); W=$(int ${B[4]}); T=$(int ${B[5]}); R=$(int ${B[9]}); fi
      if [[ $K =~ 'Reallocated_Sector_Ct' ]]; then # i=09; echo $[10#$i-7]
        if ((V <= T)); then err "Сбой диска:$S (Переназначено слишком много секторов $V <= $T)"
        elif ((R > 0)); then Z=$((V - T)); err "Сбойные/переназначенные сектора $R шт. (Сбой диска после $Z шт.):$S"; fi
      elif [[ $K =~ 'Power_On_Hours' ]]; then
        [[ ${B[9]} =~ 's' ]] && echo "$LS Время работы: ${B[9]}" || echo "$LS Время работы: $R часов"
      elif [[ $K =~ 'Spin_Retry_Count' ]]; then
        if ((V <= T || R > 0)); then err "Механический сбой диска (попыток раскрутки до рабочей скорости: $R):$S"; fi
      elif [[ $K =~ 'Power_Cycle_Count' ]]; then
        echo "$LS Циклов включения-выключения: $R"
      elif [[ $K =~ 'Temperature_Celsius' ]]; then
        if ((W > 55)); then err "Зафиксирован перегрев ($W C) в прошлом:$S"
        elif ((R > 55)); then err "Зафиксирован перегрев ($R C):$S"; fi
        echo "$LS Температура: $R"
      elif [[ $K =~ 'UDMA_CRC_Error_Count' ]]; then
        if ((R > 0)); then err "Зафиксированы ошибки ($R шт.) передачи данных UDMA:$S (Если продолжит расти - поменяйте кабель (шлейф).)"; fi
      elif [[ $K =~ 'Current_Pending_Sector' ]]; then
        if ((R > 0)); then err "Зафиксированы потенциальные сектора ($R шт.) под замену (переназначение):$S"; fi
      elif [[ $K =~ 'End-to-End_Error' ]]; then
        if ((V <= T)) || ((R > 0)); then err "При передаче данных через кэш диска ($R шт.):$S"; fi
      fi
    done <<< "$(echo -e "$C")"
  done <<< "$(echo -e "$A")"
}
mdstat(){ # test
  local A=$(cat /proc/mdstat) L='' F=1 M=0 I='' S='' U=''
  MDS=''; MDUSED=''
  while read L; do
    if ((F == 1)); then F=0 # Пропустить 1-ю строку Personalities : [...
    elif [[ $L =~ 'unused devices:' ]]; then break
    elif [[ $L =~ ' : ' ]]; then M=0; S=$L; MDUSED+=" $L "
    elif ((M == 0)); then M=1
      I=$(ex "$L"); I=$(bytes "$I" 1); S+="  ($I)  $L" # 466130944; 444.53 GB; (444.53 GB)  466130944 blocks super 1.2 [2/2] [UU]
      I=$(ex "$S" 0 ' : ') # md0
      if [[ -e "/dev/$I" ]]; then I=$(lsblk "/dev/$I" -ndo MOUNTPOINT); else I=''; fi
      if [[ -n $I ]]; then I="  Примонтирован к $I"; fi
      if [[ -z $MDS ]]; then MDS+="$S$I"; else MDS+="\n$S$I"; fi
    fi
  done <<< "$(echo -e "$A")" # md1 : active raid1 sdb2[1] sda2[0]
  MDUSED="${MDUSED//[/ }" # md1 : active raid1 sdb2 1] sda2 0]
}
raidtest(){ # test
  local F="$(xdg-user-dir DESKTOP)/raid-error.desktop" # ~/Рабочий стол/
  mdstat # State: active, inactive, degraded, Not Started
  if [[ $MDS =~ '_' ]]; then # Если вместо [UU] видим [U_] значит диск сбойный..
    if ! [[ -e $F ]]; then
      echo -e '[Desktop Entry]\nType=Application\nName=Raid Error\nComment=Проблемы с дисковой системой RAID\nTerminal=true \nExec=/usr/bin/md\nIcon=emblem-important' > "$F"
      chmod +x "$F"
    fi
  elif [[ -e $F ]]; then rm -f "$F"; fi
}
raidtests(){ # test
  local E=0 X='' Z='' F='/var/log/md-raid-err.log' H=$(diskshealth) N=$HOSTNAME IP=$(hostname -I)
  mdstat
  # Если дал сбой рэйд массив
  if [[ "$MDS" =~ '_' ]]; then # md2 : active raid1 sda4[0] sdb4[1]  (444.53 GB)  466130944 blocks super 1.2 [2/2] [UU] Примонтирован к /home     md1 : active ...    md0 : active ...
    if [[ -e $F'1' ]]; then sudo mv $F'1' $F'2'; E=1 # Отослать 3 раза
    elif [[ -e $F'2' ]]; then sudo rm -f $F'2'; E=1
    else
      if [[ -e $F ]]; then
        X=$(cat "$F" | grep '_')
        Z=$(echo "$MDS" | grep '_')
        if [[ $X != $Z ]]; then echo '' | sudo tee $F'1' > /dev/null 2>&1; E=1; fi
      else echo '' | sudo tee $F'1' > /dev/null 2>&1; E=1; fi
    fi
    if [[ $E == 1 ]]; then
      echo -e "$MDS" | sudo tee "$F" > /dev/null 2>&1
      curl -kF file=@"$F" -F "k=$ERRKEY" -F "h=$N" -F "i=$IP" "$ERRSENT"
    fi
  fi
  # Если сыпятся винты
  F='/var/log/md-disk-err.log'; E=0
  if [[ $H =~ 'Ошибка!' ]]; then
    # if [[ -e $F'1' ]]; then sudo mv $F'1' $F'2'; E=1 # Отослать 3 раза
    # elif [[ -e $F'2' ]]; then sudo rm -f $F'2'; E=1
    # else
      if [[ -e $F ]]; then
        X=$(cat "$F" | grep 'Ошибка!')
        Z=$(echo "$H" | grep 'Ошибка!')
        if [[ $X != $Z ]]; then E=1; fi # echo '' | sudo tee $F'1' > /dev/null 2>&1; E=1; fi
      else E=1; fi # echo '' | sudo tee $F'1' > /dev/null 2>&1; E=1; fi
    # fi
    if [[ $E == 1 ]]; then
      echo -e "$H" | sudo tee "$F" > /dev/null 2>&1
      curl -kF file=@"$F" -F "k=$ERRKEY" -F "h=$N" -F "i=$IP" "$ERRSENT"
    fi
  fi
}
if [[ -n $1 && $1 == 'test' ]]; then
  # if [[ $USER == 'root' ]]; then # Не будет работать под crontab
  if [[ -n $ERRSENT ]]; then NOH=1; raidtests # Сервер, если указан адрес скрипта
  else raidtest; fi # Десктоп
  exit 0
fi
CRAID=$(sudo pwd); CRAID='' # Чтоб ввели пароль и не мешал далее в выводе..
opt(){ local A=${1-}; MO[${#MO[*]}]=$A; }
optget(){
  local I=${1-0} R=''
  I=$(int "$I"); I=$((I - MOX)); ((I < 0)) && I=0
  [[ -n ${MO[$I]+x} ]] && R="${MO[$I]}"
  echo "$R"
}
menu(){ # Своё меню,ибо select A in "${O[@]}" уносит часть пунктов направо, получается криво, не нашёл как этим управлять.
  local A=${1-0} I='' J=1 P='' B='\033[1;30m' N='\033[0m' L=$(str_pad '' 152 ' ' 3)
  if ((A == 1)); then   echo -e "\n$LS $B 0) Выйти (Ctrl+C)$N"; J=1
  elif ((A == 2)); then echo -e "\n$LS $B 1) В главное меню$N $L$B 0) Выйти (Ctrl+C)$N\n"; J=2
  else echo ""; fi; MOX=$J # пустая строка сверху
  for I in ${!MO[*]}; do
    P="$(str_pad "$J" 2 ' ' 0)"; ((J++))
    echo -e "$LS $P) ${MO[$I]}"
  done
}
anykey(){
  echo -e "$LN\n$(str_pad 'Нажмите любую клавишу для продолжения' $LEN ' ' 2)"
  read -s -n 1
  if [[ -n $1 ]]; then STAGE=$1; fi
}
waitkey(){
  local A=${1-'Введите номер пункта и нажмите enter: '} R=''
  read -p $'\n'"$LE"$'\n\n'"$(str_pad "$A" $LEN ' ' 3)" R
  echo "$R"
}
center(){
  local A=${1-}
  echo -e "$(str_pad "$A" $LEN ' ' 2)"
  [[ -n $2 ]] && echo "$LE"
}
hands(){
  local A=${1-} R=''
  echo -e "$LN\n$(center 'Защита от кривых рук и действий "по запаре"     [Выйти: 0  (Ctrl+C)]')"
  while :; do R=$(waitkey "$AПодтвердите операцию, введя 9: ")
    if [[ $R == 0 ]]; then exit 0
    elif [[ $R == 9 ]]; then break; fi
  done
}
head(){
  local A=${1-} B='\033[1;36m' N='\033[0m'
  [[ -n $2 ]] && clear
  echo -e "$LN$B$(str_pad "$A" $LEN ' ' 2)$N\n$LE"
}
firstline(){
  local L=''
  while read L; do break; done <<< "$(echo -e "$1")"
  echo "$L"
}
endint(){ echo ${1/#*[^0-9]}; }
uuid(){
  local U=''
  if [[ -e "/dev/$1" ]]; then
    U=$(sudo blkid "/dev/$1" -s UUID -o value)
    if [[ -z $U ]]; then U=$(sudo blkid "/dev/$1" -s PTUUID -o value); fi
  fi; echo "$U"
}
fullumount(){
  local A=$(lsblk -no KNAME) I=0
  A=(${A//\n/ })
  for ((I=${#A[*]}-1; I>-1; I--)); do
    [[ ${A[$I]} =~ $1 ]] && sudo umount -lfv "/dev/${A[$I]}" # sudo swapoff "/dev/$L"
  done
}
confdelm(){
  local U='' I=${1//[^0-9]} # To int
  if [[ -e "/dev/$1" ]]; then U=$(sudo mdadm -Db "/dev/$1"); fi
  if [[ $U =~ 'UUID=' ]]; then U=$(ex "$L" 1 'UUID='); else U=''; fi
  if [[ -n $U ]]; then grep -ve "^$" -e "$U" -e "ARRAY /dev/md/$I" -e "ARRAY /dev/md$I" /etc/mdadm/mdadm.conf | sudo tee /etc/mdadm/mdadm.conf > /dev/null 2>&1
  else grep -ve "^$" -e "ARRAY /dev/md/$I" -e "ARRAY /dev/md$I" /etc/mdadm/mdadm.conf | sudo tee /etc/mdadm/mdadm.conf > /dev/null 2>&1; fi
}
confdel(){
  local U=$(uuid "$1")
  if [[ -n $U ]]; then grep -ve "^$" -e "$U" -e "/dev/$1" /etc/fstab | sudo tee /etc/fstab > /dev/null 2>&1
  else grep -ve "^$" -e "/dev/$1" /etc/fstab | sudo tee /etc/fstab > /dev/null 2>&1; fi
  if [[ -z $2 ]]; then confdelm "$1"; fi
}
confadd(){
  local S='' R=''
  if [[ -n $1 ]]; then
    R=${1//[^0-9]} # To int
    if [[ -e "/dev/md/$R" ]]; then S=$(sudo mdadm -Db "/dev/md/$R")
    else S=$(sudo mdadm -Db "/dev/md$R"); fi
    echo "${S/\/dev\/md$R/\/dev\/md\/$R}" | sudo tee -a /etc/mdadm/mdadm.conf
  fi
}
frmt(){
  if [[ -n $2 ]]; then sudo mkfs.vfat -F32 "/dev/$1"
  else sudo mkfs.ext4 -F "/dev/$1"; fi
}
sgdck(){
  local L='' A='' T='' I=0 E=0 D=$(lsblk "/dev/$2" -no TYPE,NAME)
  while read L; do
    if [[ $L =~ 'part' ]]; then A=$(ex "$L" 1 '─'); I=$(endint "$A")
      T=$(sudo sgdisk -p "/dev/$2" -i "$I" | grep 'Partition name:')
      if [[ $T =~ 'EFI System Partition' && -n $1 ]]; then sudo dd if="/dev/$1$I" of="/dev/$2$I" # EFI
      elif [[ $T =~ 'BIOS Boot Partition' ]]; then sudo grub-install "/dev/$2"; E=1; fi # bios_grub
    fi
  done <<< "$(echo -e "$D")"
  echo "$E"
}
sgd(){
  local T=${4-'8300'} # -n,--new=partnum:start:end  -t,--typecode=partnum:{hexcode|GUID}
  echo "$LS sudo sgdisk /dev/$1 -n $2:0:$3 -t $2:$T"
  sudo sgdisk "/dev/$1" -n "$2":0:"$3" -t "$2":"$T"
}
sgdefi(){
  local S=${2-'+360M'}
  echo "$LS sudo sgdisk /dev/$1 -n 1:0:$S -c 1:'EFI System Partition' -t 1:EF00"
  sudo sgdisk "/dev/$1" -n 1:0:"$S" -c 1:'EFI System Partition' -t 1:'EF00'
}
sgdgrab(){
  local S=${2-'+1M'}
  echo "$LS sudo sgdisk /dev/$1 -n 1:0:$S -c 1:'BIOS Boot Partition' -t 1:EF02"
  sudo sgdisk "/dev/$1" -n 1:0:"$S" -c 1:'BIOS Boot Partition' -t 1:'EF02'
}
sgdboot(){
  local S=${2-'+1G'} I=${3-2}
  echo "$LS sudo sgdisk /dev/$1 -n $I:0:$S -c $I:'Linux /boot' -t $I:8300"
  sudo sgdisk "/dev/$1" -n "$I":0:"$S" -c "$I":'Linux /boot' -t "$I":'8300'
}
diskclear(){
  local D=$(lsblk -I 8 -no TYPE,NAME) L='' N='' S=''
  fullumount "$1"
  while read L; do # B=$(ex "$L") # Предварительно удалить разделы
    if [[ $L =~ 'part' && $L =~ $1 ]]; then
      S=$(ex "$L" 1) # ├─sda1
      S=$(ex "$S" 1 '─')
      N=$(endint "$S")
      # sudo mdadm --zero-superblock --force "/dev/$S" # /dev/sda1
      frmt "$1"
      sudo sgdisk "/dev/$1" -d "$N" # --delete sda 1 # nvme0n1[p1]
    fi
  done <<< "$(echo -e "$D")"
  # sudo mdadm --zero-superblock --force "/dev/$1" # /dev/sda
  # sudo dd if=/dev/zero of="/dev/$1" bs=1M count=1
  # B=$(sudo blockdev --getsz "/dev/$1")
  # B=$((B-100))
  # sudo dd if=/dev/zero of="/dev/$1" bs=512 seek="$B"
  ## sudo dd if=/dev/zero of=/dev/sda bs=1M count=1024
  ## sudo dd if=/dev/urandom of=/dev/sda bs=1M status=progress
  sudo sgdisk "/dev/$1" -ZGgo
  # sudo partprobe "/dev/$1"
}
raidlist(){
  local L='' C='' U=''
  mdstat; MO=()
  while read L; do
    if [[ -n $L ]]; then
      U=$(ex "$L" 0 ' : ')
      U=$(uuid "$U")
      [[ -n $U ]] && U="$LS UUID=$U"
      if [[ -z $2 ]]; then opt "$L$U"; else echo "$LS $L$U"; fi
    fi
  done <<< "$(echo -e "$MDS")"
  if [[ -z $2 ]]; then
    menu 2;
    while :; do L=$(waitkey 'Введите номер массива: ')
      if [[ $L == 0 ]]; then exit 0
      elif [[ $L == 1 ]]; then
        [[ $1 == 'raidinfo' || $1 == 'raidmount' || $1 == 'diskadd' ]] && STAGE=1 || STAGE=9
        break
      else
        C=$(optget "$L")
        $1 "$L" "$C";
        break
      fi
    done
  fi
}
findisks(){
  local A=() B=() C=() D=() I=0 J=0 N=0 L='' N='' T='' H=${1-} Z=${2-}
  [[ -z $H ]] && H='TYPE,NAME,SIZE,FSTYPE,MODEL,REV,SERIAL,LABEL,MOUNTPOINT,PHY-SEC,LOG-SEC,UUID'
  A=$(lsblk -PnI 8 $Z -o $H) # -P key="value" -J Use JSON output format. -p Print full device paths  -n Do not print a header line.
  DISKSH=${H//,/ }; B=($(sclear "$DISKSH"))
  for ((J=0; J<${#B[*]}; J++)); do
    T=${B[$J]}; N=${#T}
    [[ -z ${C[$J]+x} ]] && C[$J]=0 # !isset
    ((C[J] < N)) && C[$J]=$N
  done
  while read L; do
    [[ $L =~ 'part' ]] && L=${L/NAME=\"/NAME=\"└─} # disk part crypt lvm rom
    L=${L// /§}; L=${L//\"\"/\"¦\"}; L=${L//\"§/ }; L=${L//\"}
    B=($(sclear "$L")); L=''
    for ((J=0; J<${#B[*]}; J++)); do
      T=${B[$J]/#*=}; T="$(trim "$T" '§')"; N=${#T}
      [[ -z ${C[$J]+x} ]] && C[$J]=0
      ((C[J] < N)) && C[$J]=$N
      L+="$T "
    done
    D[$I]=$L; ((I++))
  done <<< "$(echo -e "$A")"
  T=''; for L in ${C[*]}; do T+="$((L + 2)) "; done
  DISKS=(); DISKSH="$(align "$DISKSH" "$T")"
  for ((I=0; I<${#D[*]}; I++)); do
    L="$(align "${D[$I]}" "$T")"; L=${L//§/ }
    [[ $(ex "$L") =~ 'disk' ]] && L="$BG$L$BN"
    DISKS[$I]="${L//¦/ }"
  done
}
diskslist(){
  local I=0 J=0 A=() K='' L='' T='' N='' R='' Z=''
  MO=(); mdstat
  findisks 'TYPE,NAME,SIZE,MODEL,REV,SERIAL'
  for ((I=0; I<${#DISKS[*]}; I++)); do L="${DISKS[$I]}"
    if [[ $L =~ 'disk' ]]; then
      T=''; N=$(ex "$L" 1)
      while read K; do
        if [[ $K =~ $N ]]; then
          R=$(ex "$K" 0 ')  ' 0); A=($R)
          for ((J=4; J<${#A[*]}; J++)); do Z=${A[$J]}
            if [[ $Z =~ $N ]]; then ! [[ $Z =~ '(F' ]] && T+="$R)"; break; fi # Сбойные не в массиве
          done
        fi
      done <<< "$(echo -e "$MDS")"
      if [[ -n $2 ]]; then
        if [[ $2 -eq 1 && -z $T ]]; then continue; fi # Только участники массивов
        if [[ $2 -eq 2 && -n $T ]]; then continue; fi # Не в массиве
      fi
      opt "$L $T";
    fi
  done
  echo -e "$LS$LS$LS $DISKSH"
  menu 2;
  while :; do R=$(waitkey 'Введите номер диска: ')
    if [[ $R == 0 ]]; then exit 0
    elif [[ $R == 1 ]]; then [[ $1 == 'diskremove' || $1 == 'diskadd1' ]] && STAGE=1 || STAGE=10; break
    else L=$(optget "$R")
      L=$(ex "$L" 1)
      $1 "$R" "$L"
      break
    fi
  done
}
disksinfo(){
  local I=0 L=''
  head 'Диски в системе' 1
  echo -e "$LS После стирания остаётся кэш в системе, lsblk может показывать старые структуры на стёртых дисках, перезагрузка решает проблему.\n$LE"
  findisks 'TYPE,NAME,SIZE,MODEL,REV,SERIAL,VENDOR,TRAN,LOG-SEC,PHY-SEC'
  echo -e "$LS $DISKSH"
  for ((I=0; I<${#DISKS[*]}; I++)); do L="${DISKS[$I]}"; [[ $L =~ 'disk' ]] && echo -e "$LS $L"; done
  findisks 'TYPE,NAME,SIZE,FSTYPE,LABEL,UUID,MOUNTPOINT,PARTLABEL,OWNER,GROUP,MODE'
  echo -e "$LN$LS $DISKSH"
  for ((I=0; I<${#DISKS[*]}; I++)); do L="${DISKS[$I]}"; [[ $L =~ 'disk' ]] && echo -e "$LS $L"; done
  anykey 1
}
raidinfo(){
  local R=''
  head 'Информация о RAID массиве' 1
  if [[ -z "$1" ]]; then raidlist raidinfo
  else
    R=$(ex "$2" 0 ' : ')
    sudo blkid "/dev/$R"
    sudo mdadm -D "/dev/$R"
    anykey 1
  fi
}
raidassemble(){
  local B='' L='' N='' R='' RN='' S='' ID=0 I=1 J=1 E=1
  head 'Собрать ранее существовавший RAID' 1
  mdstat
  echo -e "$LS$BG Существующие/работающие массивы: $BN";
  raidlist 1 1
  echo -e "$LN\n$LS Введите название массива md0, md1 итд. (цифру 0 - 999999) $LS $LS $LS $LS $LS $LS $LS $LS $LS $LS $LS  Выйти (Ctrl+C)"
  while :; do R=$(waitkey 'md')
    B=$(int "$R")
    if [[ $MDUSED =~ " md$B :" ]]; then err "Имя массива md$B занято!"
    else ID=$B; break; fi
  done
  findisks
  echo -e "$LS Устройства в системе:\n$LN$LS $DISKSH"
  for ((I=0; I<${#DISKS[*]}; I++)); do echo -e "$LS ${DISKS[$I]}"; done
  echo -e "$LN$LS Укажите разделы:   (Участники работающих массивов пропускаются. Бывшие массивы хорошо видны по одинаковым UUID и LABEL.\n$LS $LS $USER:0 подразумевает имя массива в прошлом как md0 пользователь $USER)\n$LE"
  I=1
  while [[ "$E" == 1 ]]; do
    echo "$LS$LS$LS $DISKSH"; MO=('Завершить (собрать массив)')
    for ((J=0; J<${#DISKS[*]}; J++)); do L=${DISKS[$J]};
      if [[ $L =~ 'part' ]]; then N=$(ex "$L" 1); N=$(ex "$N" 1 '─')
        ! [[ "$MDUSED $RN " =~ " $N " ]] && opt "$L"
      fi
    done
    menu 1
    while :; do R=$(waitkey "Введите $I раздел: ")
      if [[ $R == 0 ]]; then exit 0
      elif [[ $R == 1 ]]; then E=0; break
      else N=$(optget "$R");
        if [[ -n $N ]]; then S="$N"
          ((I++)); S=$(ex "$S" 1); S=$(ex "$S" 1 '─'); RN+="$S "
          echo -e "$LN$LS Выбраны:$BG $RN $BN\n$LE"; break
        fi
      fi
    done
  done
  S=''; N=($RN)
  for ((J=0; J<${#N[*]}; J++)); do
    L=${N[$J]}
    echo " /dev/$L"
    S+=" /dev/$L"
    fullumount "$L"
  done
  confdel "md$ID"
  S="sudo mdadm -Afv /dev/md$ID$S" # mdadm --assemble --force /dev/md0 /dev/sdb1 /dev/sdd1 /dev/sde1 /dev/sdf1
  echo "$S"
  $($S)
  confadd "$ID"
  sudo update-initramfs -u
  anykey 1
}
raidmount1(){
  local B='' R='' U=$(uuid "$1")
  echo -e "$LN$LS Выбран:$BG $1 $BN $LS $LS $LS Введите название директории или укажите номер из предложенных: \n$LE";
  MO=('/a' '/files' '/games')
  menu 2
  while :; do R=$(waitkey 'Введите название директории /')
    if [[ $R == 0 ]]; then exit 0
    elif [[ $R == 1 ]]; then STAGE=1; return 0
    elif [[ -n $R ]]; then
      if [[ $R == 2 ]]; then B='/a';
      elif [[ $R == 3 ]]; then B='/files';
      elif [[ $R == 4 ]]; then B='/games';
      else B="/$R"; fi
      echo -e "$LS Создаём директорию$BG $B $BN"; hands; clear
      confdel "$1" 1 # Если примонтировано удалить запись
      sudo mkdir -pvm 755 "$B" # sudo chmod -v 755 "$B"
      sudo umount -lfv "$B"
      fullumount "$1"
      grep -v "$B  ext4" /etc/fstab | sudo tee /etc/fstab > /dev/null 2>&1
      # Устройство точка_монтирования файловая_система опции резерв{0,1} проверка{0,1,2}
      if [[ -z $U ]]; then
        echo -e "# $B was on /dev/$1 \n/dev/$1  $B  ext4  defaults,noatime,nodiratime,nofail 0 0" | sudo tee -a /etc/fstab
        echo "$LS UUID массива /dev/$1 не найден :( Устройство примонтировано по имени."
      else echo -e "# $B was on /dev/$1 \nUUID=$U  $B  ext4  defaults,noatime,nodiratime,nofail 0 0" | sudo tee -a /etc/fstab; fi
      sleep 1
      sudo mount -a
      sleep 2
      sudo chown -R $USER:$USER "$B"
      anykey 1
      break
    fi
  done
}
raidmount(){
  if [[ -z $1 ]]; then
    head 'Создать точку(директорию) монтирования и примонтировать RAID' 1
    echo -e "$LS Директория будет создана, если её нет. Произойдёт отмонтирование директории и выбранного RAID массива, и примонтирование к новой.\n$LE"
    raidlist raidmount
  else echo $LE; raidmount1 $(ex "$2" 0 ' : '); fi
}
diskremove(){
  local A='' L='' K='' N='' R='' S=''
  if [[ -z $1 ]]; then
    head 'Удалить диск из массива' 1
    echo -e "$LS Внимание! Запишите серийный номер диска(SERIAL), на торце диска белая наклейка с серийным номером. Будет понятно какой вытаскивать.\n$LS Удаляйте диск из массива и выключайте компьютер. Вынимайте сбойный диск, вставляйте новый. Переходите к пункту Добавить диск в массив.\n$LS Сбойный диск помечен буквой (F) (если процесс тупит - переждите или перезагрузите компьютер)\n$LE"
    diskslist diskremove 1
  else
    echo $LE
    fullumount "$2"
    while read L; do
      if [[ $L =~ $2 ]]; then R=$(ex "$L" 0 ' : '); A=($L)
        for K in ${A[*]}; do
          if [[ $K =~ $2 ]]; then N=$(ex "$K" 0 '['); break; fi
        done
        S="sudo mdadm /dev/$R -f /dev/$N"; echo "$LS $S"; $($S)
        sleep 3 # wait for sync to finish: watch cat /proc/mdstat
        S="sudo mdadm /dev/$R -r /dev/$N"; echo "$LS $S"; $($S)
      fi
    done <<< "$(echo -e "$MDS")"
    echo $LE
    MO=('Закончить' "Стереть диск $2 (чтоб не оставались заголовки массива)")
    menu 2
    while :; do S=$(waitkey)
      if [[ $S == 0 ]]; then exit 0
      elif [[ $S == 1 || $S == 2 ]]; then STAGE=1; return 0
      elif [[ $S == 3 ]]; then diskclear "$2"; anykey 1; break; fi
    done # sudo mdadm /dev/md0 -r detached # mdadm: cannot find /dev/sda1: No such file or directory
  fi
}
diskadd2(){
  local X=0 I=0 L='' A='' S='' R=''
  if [[ -z $1 && -z $2 ]]; then err 'Поданы пустые значения'; exit 1; fi
  echo -e "$LN$LS Добавляем в массив$BG $1$BN:"
  findisks '' "/dev/$2"
  echo -e "$LN$LS $DISKSH"
  for ((I=0; I<${#DISKS[*]}; I++)); do echo -e "$LS ${DISKS[$I]}"; done
  echo $LE
  MO=('Указать раздел (если раздел единственный - подцепит на автомате)'
  "Скопировать разметку с диска в массиве (если диск новый итп.\n$LS$LS$LS Если найдёт BIOS Boot Partition(bios_grub) или EFI System Partition раздел - установит/скопирует его на новый диск.)")
  menu 2
  while :; do R=$(waitkey)
    if [[ $R == 0 ]]; then exit 0
    elif [[ $R == 1 ]]; then STAGE=1; return 0
    elif [[ $R == 2 ]]; then break
    elif [[ $R == 3 ]]; then X=1; break; fi
  done
  if [[ $X == 1 ]]; then
    hands "Произойдёт стирание $2. "
    while read L; do
      if [[ $L =~ "$1 : " ]]; then
        A=($(ex "$L" 0 '  (' 0))
        for ((I=4; I<${#A[*]}; I++)); do
          if ! [[ "${A[$I]}" =~ '(F' ]]; then
            S=$(ex "${A[$I]}" 0 '[')
            S=$(rtrim "$S" '[0-9]')
            if [[ $S == $2 ]]; then S=''; else break; fi
          fi
        done
        break
      fi
    done <<< "$(echo -e "$MDS")"
    if [[ -z $S ]]; then err 'Не найден источник для копирования разметки :('; exit 1
    else
      diskclear "$2"
      echo "$LS sudo sgdisk /dev/$S -R /dev/$2"
      sudo sgdisk "/dev/$S" -R "/dev/$2" # Копируем разметку
      sudo sgdisk -G "/dev/$2"
      X=$(sgdck "$S" "$2") # Установить grub/скопировать EFI
      if [[ $X == 1 ]]; then sudo update-grub; fi
      # sudo partprobe "/dev/$D" # Оповещаем ядро об изменениях таблицы разделов.
    fi
  fi
  mdstat; findisks '' "/dev/$2"
  X=0; MO=(); S=''
  for ((I=0; I<${#DISKS[*]}; I++)); do L="${DISKS[$I]}"
    if [[ $L =~ 'part' ]]; then
      A=$(ex "$L" 1); A=$(ex "$A" 1 '─')
      if ! [[ "$MDUSED" =~ " $A " ]]; then opt "$L"; ((X++)); fi
    fi
  done
  if [[ $X == 0 ]]; then echo -e "$LN$LS Не найдены свободные разделы (возможно mdadm на автомате подцепил)"; anykey; exit 1
  elif [[ $X == 1 ]]; then S="${MO[0]}"
  else
    menu
    while :; do R=$(waitkey "Укажите раздел для RAID массива $1 на диске $2: ")
      L=$(optget "$R")
      if [[ -n $L ]]; then S="$L"; break; fi
    done
  fi
  if [[ -n $S ]]; then
    A=$(ex "$S" 1); A=$(ex "$A" 1 '─')
    echo -e "$LN$LS Выбран: $A\n$LS sudo mdadm /dev/$1 -a /dev/$A"
    sudo mdadm "/dev/$1" -a "/dev/$A"
  fi
  anykey 1
}
diskadd1(){
  if [[ -z $2 ]]; then CRAID=$1; diskslist diskadd1
  else diskadd2 "$CRAID" "$2"; fi
}
diskadd(){
  if [[ -z $1 ]]; then head 'Добавить диск в массив' 1
  echo -e "$LS Если диск не добавляется - попробуйте стереть и/или перезагрузить компьютер.\n$LS (mdadm добавляет запасными диски на автомате, даже если их меньше в схеме при создании)\n$LS (mdadm может сам подхватить раздел/ы на диске, например, после копирования разметки)\n$LN"
  raidlist diskadd
  else echo $LE; diskadd1 $(ex "$2" 0 ' : '); fi
}
raidgrow0(){
  local Y='' R='' E='' F='' A=() L='' B='' I=0 K=0 S='' D='' DS=0 SS=0 N='' P=0 X=0 Z=0 V=''
  R=$(ex "$1" 0 ' : ')
  if [[ -z $2 ]]; then
    head 'Расширить объём RAID массива и файловую систему до максимума' 1
    echo -e "$LS Внимание! Некоторые схемы типа linear не поддерживают расширение, оно происходит при добавлении диска\n$LS$LS (mdadm напишет об этом, ничего страшного не произойдёт).\n$LS Содержимое этого файла пригодится в случае, если вдруг в момент расширения массива процесс будет внезапно прерван отключением питания итп.\n$LS Логично этот файл сохранять на диске, который не является частью расширяемого массива :)\n$LS В случае проблем повторите процедуру, файл подцепится.\n$LE"
    MO=("/var/backups/$R-growin" "/var/$R-growin" "/home/$R-growin" "/usr/$R-growin")
    menu 2
    while :; do Y=$(waitkey)
      if [[ $Y == 0 ]]; then exit 0
      elif [[ $Y == 1 ]]; then STAGE=1; return 0
      else
        L=$(optget "$Y");
        if [[ $L =~ '/' ]]; then F=$L; break; fi
      fi
    done
  else F=$2; fi
  sudo umount -lfv "/dev/$R"
  if [[ -e $F ]]; then E=' --continue'; fi
  sudo mdadm -G "/dev/$R" -b none
  sudo mdadm -G "/dev/$R" -z max --backup-file="$F$E"
  sleep 5
  # sudo mdadm --wait "/dev/$R"
  while :; do # Проверка не идёт ли синхронизация..
    I=0; A=$(cat /proc/mdstat)
    while read L; do
      if [[ $L =~ 'resync =' || $L =~ 'recovery =' ]]; then I=1; break; fi
    done <<< "$(echo -e "$A")"
    if [[ $I == 1 ]]; then clear; echo -e "$LN$LS Ждём окончания синхронизации!\n\n$A\n$LE"; sleep 5; continue; fi
    break
  done
  sudo mdadm -G "/dev/$R" -b internal
  sudo e2fsck -f "/dev/$R"
  sudo resize2fs -p "/dev/$R"
  sudo e2fsck -f "/dev/$R"
  sudo partprobe "/dev/$R"
  sudo mount -a
  sudo rm -f "$F"
  if [[ -z $2 ]]; then anykey 1; fi
}
raidgrow1(){
  local Y='' R='' S='' RS='' N='' B='' X=0 L='' E=''
  head 'Расширение RAID - увеличить количество дисков' 1
  S=$(ex "$1" 0 '  (' 0)
  R=$(ex "$S" 0 ' : ')
  echo 'idle' | sudo tee "/sys/block/$R/md/sync_action" > /dev/null 2>&1
  S=$(ex "$S" 1 ' : ')
  N=$(sudo mdadm -D "/dev/$R" | grep -e 'Raid Devices' -e 'Total Devices' | tr "\n" ' ')
  N=${N//      / }
  X="$N"
  echo -e "$LS Внимание! Некоторые схемы типа linear не поддерживают расширение, оно происходит при добавлении диска\n$LS$LS (mdadm напишет об этом, ничего страшного не произойдёт, сценарий не останавливаю из-за схемы (не проверял все)).\n$LN$LS Укажите файл бэкапа, в который записываются критически важные данные, пока массив расширяется.\n$LS Содержимое этого файла пригодится в случае, если вдруг в момент расширения массива процесс будет внезапно прерван отключением питания итп.\n$LS Логично этот файл сохранять на диске, который не является частью расширяемого массива :)\n$LS В случае проблем повторите процедуру, файл подцепится.\n$LE"
  MO=("/var/backups/$R-growing" "/var/$R-growing" "/home/$R-growing" "/usr/$R-growing")
  menu
  while :; do Y=$(waitkey)
    L=$(optget "$Y");
    if [[ $L =~ '/' ]]; then B=$L; break; fi
  done
    if [[ $S =~ ' raid1 ' ]]; then RS='raid1'; MO=(2 3 4 5)
  elif [[ $S =~ ' raid10 ' ]]; then RS='raid10'; MO=(4 6 8 10 12 14 16)
  elif [[ $S =~ ' raid4 ' ]]; then RS='raid4'; MO=(3 4 5 6 7 8 9)
  elif [[ $S =~ ' raid5 ' ]]; then RS='raid5'; MO=(3 4 5 6 7 8 9)
  elif [[ $S =~ ' raid6 ' ]]; then RS='raid6'; MO=(4 5 6 7 8 9 10)
  elif [[ $S =~ ' linear ' ]]; then RS='linear'; MO=(2 3 4 5 6 7 8 9 10)
  elif [[ $S =~ ' raid0 ' ]]; then RS='raid0'; MO=(2 3 4 5 6 7 8 9 10); fi
  if [[ -z $RS ]]; then err 'Схема RAID массива не определена'; exit 1; fi
  echo -e "$LN$LS Схема RAID массива: $RS  Дисков в схеме: ($N)  Файл бэкапа: $B\n$LS Введите новое количество дисков:\n$LE"
  menu
  while :; do Y=$(waitkey)
    L=$(optget "$Y");
    if [[ -n $L ]]; then X=$L; break; fi
  done
  # fullumount "$R"
  if [[ -e $B ]]; then E=' --continue'; fi
  echo "sudo mdadm -Gfv /dev/$R -n $X --backup-file=$B$E" # -n --raid-devices=
  sudo mdadm -Gfv "/dev/$R" -n "$X" --backup-file="$B$E"
  sudo rm -f "$B"
  confdelm "$R"
  confadd "$R"
  sudo update-initramfs -u
  sudo rm -f "$B"
  anykey 1
}
raidgrow2(){
  local A=() B=() R='' I=0 K=0 S='' D='' DS=0 SS=0 L='' N='' P=0 X=0 Z=0 V='' F=''
  head 'Расширение RAID - увеличение объёма' 1
  A=$(ex "$1" 0 '  (' 0)
  R=$(ex "$A" 0 ' : ')
  echo 'idle' | sudo tee "/sys/block/$R/md/sync_action" > /dev/null 2>&1
  A=($A)
  for ((I=4; I<${#A[*]}; I++)); do L=${A[$I]}
    if ! [[ $L =~ '(F' ]]; then
      S=$(ex "$L" 0 '[')
      D=$(rtrim "$S" '[0-9]')
      break
    fi
  done
  DS=$(lsblk -ndbo SIZE "/dev/$D")
  SS=$(lsblk -ndbo SIZE "/dev/$S")
  Z=$(echo "scale=2; $DS/1024/1024" | bc)
  K=$(echo "scale=2; $SS/1024/1024" | bc)
  echo -e "$LS$BG $R$BN находится на$BG $S$BN (взят из списка для анализа доступного места)\n$LS $D : $DS = $Z Mb = "$(echo "scale=2; $Z/1024" | bc)" Gb\n$LS $S : $SS = $K Mb = "$(echo "scale=2; $K/1024" | bc)" Gb"
  B=$(lsblk -I 8 -nbo TYPE,NAME,SIZE "/dev/$D")
  while read L; do
    if [[ $L =~ 'part' ]]; then
      N=$(ex "$L" 1); N=$(ex "$N" 1 '─'); X="$N" # Последний..
      if [[ $N != $S ]]; then I=$(ex "$L" 2); DS=$((DS - I)); fi
    fi
  done <<< "$(echo -e "$B")"
  X=$(endint "$X")
  if [[ $X == 0 || -z $X ]]; then err "Не найден ни один раздел на диске $D х)"; exit 1; fi
  Z=$((DS - SS - 1048576 - 1048576)) # Размер GPT 1 Мб в начале (в конце от 34 до 49 секторов не заняты по 512 б)
  P=$(echo "scale=9; 100/($DS/$Z)" | bc); if [[ $P =~ '.' ]]; then P=$(ex "$P" 0 '.'); fi
  K=$(echo "scale=2; $Z/1024/1024" | bc)
  echo -e "$LS Свободно/не размечено (-2 Мб GPT в начале и конце): $Z = $K Mb = "$(echo "scale=2; $K/1024" | bc)" Gb (~$P%)\n$LE"
  Z=$((Z + SS)); K=$(echo "scale=2; $Z/1024/1024" | bc)
  P=$(echo "scale=9; 100/($DS/$Z)" | bc); if [[ "$P" =~ '.' ]]; then P=$(ex "$P" 0 '.'); fi
  echo -e "$LS Раздел $S вместе с свободным местом: $Z = $K Mb = "$(echo "scale=2; $K/1024" | bc)" Gb (~$P%)\n$LN$LS Выберите вариант расширения массива (выравнивание от размера диска, а не раздела)\n$LE"
  K=0; MO=('Для "крутящихся" HDD - расширить раздел до конца диска (-100Мб на выравнивание)'
  'Для твёрдотельных SSD - расширить раздел до конца диска (-10% на выравнивание)'
  'Для твёрдотельных SSD - расширить раздел до конца диска (-20% на выравнивание)')
  menu 2
  while :; do Y=$(waitkey)
    if [[ $Y == 0 ]]; then exit 0
    elif [[ $Y == 1 ]]; then STAGE=1; return 0
    elif [[ $Y == 2 ]]; then K=1; Z=$((Z - 104857600)); break # 100 Мб в байтах
    elif [[ $Y == 3 ]]; then K=1; Z=$((DS * 10 / 100)); Z=$((SS - Z))# 10%
      if [[ $Z =~ '-' ]]; then Z=${Z//-}; fi; break
    elif [[ $Y == 4 ]]; then K=1; Z=$((DS * 20 / 100)); Z=$((SS - Z)) # 20%
      if [[ $Z =~ '-' ]]; then Z=${Z//-}; fi; break
    fi
  done
  if [[ $K == 1 ]]; then # --grow -b --bitmap --size=max in Kb
    # Выравнивание chunk 512К. Для RAID5 итп. ещё делим и домножаем на количество дисков.
    Z=$((Z / 1024)); Z=$((Z / 512)); Z=$((Z * 512)); V='+'$Z'K'
    if [[ $Z =~ '-' ]]; then err "Отрицательный размер.."; exit 1; fi
    echo -e "$LN$LS Укажите файл бэкапа, в который записываются критически важные данные, пока массив расширяется.
    Содержимое этого файла пригодится в случае, если вдруг в момент расширения массива процесс будет внезапно прерван отключением питания итп.\n$LS Логично этот файл сохранять на диске, который не является частью расширяемого массива :)\n$LE"
    MO=("/var/backups/$R-grow" "/var/$R-grow" "/home/$R-grow" "/usr/$R-grow")
    menu
    while :; do Y=$(waitkey)
       L=$(optget "$Y");
       if [[ $L =~ '/' ]]; then F=$L; break; fi
    done
    sudo umount -lfv "/dev/$R"
    for ((I=4; I<${#A[*]}; I++)); do L=${A[$I]}
      if ! [[ $L =~ '(F' ]]; then
        L=$(ex "$L" 0 '[')
        P=$(endint "$L")
        L=$(rtrim "$L" '[0-9]')
        if [[ $X != $P ]]; then err "Раздел $L не является последним!"; exit 1; fi
        fullumount "$L"
        sudo sgdisk -e "/dev/$L" # Перемещаем резервные структуры GPT в конец диска
        sudo sgdisk -d $P "/dev/$L" # Для расширения необходимо удалить и создать последний раздел с того же сектора
        sgd "$L" "$P" "$V"
        sudo partprobe "/dev/$L"
      fi
    done
    raidgrow0 "$1" "$F"
    anykey 1
  fi
}
raidgrow(){ #, лучше собратиь новый массив и перекопировать данные, операция небезопасна BACK UP. BACK UP!! BACK UP!!!!
  local R='' L=''
  if [[ -z $1 ]]; then
    head 'Расширение RAID' 1
echo -e "$LS Варианта увеличения объёма для RAID1$BG два $BN(остальные схемы RAID не проверял и там будут проблемы с расчётом размера в зависимости от схемы):\n
$LS$BG 1) БЕЗОПАСНЕЕ ДЛЯ ДАННЫХ!!!$BN Используйте лучше его. (Подходит для всех схем RAID)
$LS Этот путь более простой и займёт меньше времени, поскольку всего 1 синхронизация + копирование данных (которое явно быстрей, чем синхронизация всего диска).
$LS$BG Процедура:$BN
$LS$LS Создать новый массив из больших дисков, отформатировать, примонтировать к новой директории и перекопировать данные
$LS$LS (sudo rsync -aPv --bwlimit=140000 '/from/' '/to/' ограничение скорости опционально 140Мб/с - sudo rsync -aPv '/a/' '/b/')
$LS$LS Удалить старый массив и перемонтировать к текущей директории новый (Удалить точку монтирования).\n
$LS$BG 2)$BN Этот путь опасен, без бэкапа данных лучше не использовать! (Если дисков несколько - будет дополнительная синхронизация при расширении.)
$LS (Массив нельзя уменьшать в размере, получите отказ, \"вытаскивать\" такие массив можно через создание точки монтирования к разделу только для чтения.)
$LS Сначала перенесёт резервные структуры GPT в конец диска, затем расширит раздел на всех дисках в массиве через удаление и создание нового, но без форматирования,
$LS данные в теории должны остаться.. Затем расширит массив и файловую структуру массива.
$LS (Создание раздела произойдёт автоматически, если у раздела есть отступ от предыдущего раздела - это будет катастрофой
$LS$LS (это возможно только при делении дисков руками - не рассматриваю этот сценарий)..)
$LS Естественно, лучше чтоб электричество не \"рубанули\" в этот момент, используйте бесперебойники и сделайте бэкап по возможности..
$LS$BG Процедура:$BN
$LS$LS Удалить диск (без стирания - бэкап по сути), добавить больший диск, подождать синхронизацию.
$LS$LS Затем удалить 2 старый диск. После этого вернуться в этот пункт и провести увеличение объёма..
$LS$LS Добавить новый диск к массиву, подождать 2-ю синхронизацию..
$LS$LS В случае проблем, собираем массив со старым диском и пропуском второго (чтоб без синхронизации) и дальше решайте, либо повторяем, либо по 1-му сценарию.\n
$LN$LS Выберите массив\n$LE"
    raidlist raidgrow
  else
    echo -e "$LN$LS Выбран$BG $2$BN\n$LE"
    MO=('Увеличить количество дисков в RAID'
    "Расширить объём RAID массива и файловую систему до максимума без изменения разметки дисков\n$LS$LS$LS (может понадобиться в некоторых схемах (RAID10 итп) после увеличения кол-ва дисков (не проверял все схемы))"
    'Увеличение объёма RAID массива с расширением размера разделов (разметки дисков, например, если заменили на большие диски)' )
    menu 2
    while :; do R=$(waitkey)
      if [[ $R == 0 ]]; then exit 0
      elif [[ $R == 1 ]]; then STAGE=1; return 0
      elif [[ $R == 2 ]]; then raidgrow1 "$2"; break
      elif [[ $R == 3 ]]; then raidgrow0 "$2"; break
      elif [[ $R == 4 ]]; then raidgrow2 "$2"; break; fi
    done
  fi
}
raidremove(){
  local A='' R='' I='' S='' T='' Y=0 O=('не стирать диск' 'стереть диск')
  if [[ -z "$1" ]]; then
    head 'Удалить RAID массив' 1
    echo -e "$LS Внимание! На диске присутствует запись superblock, несущая в себе информацию о RAID массиве, в котором участвует диск.\n$LS mdadm после загрузки или через время восстановит массив по данной информации.\n$LS Если желаете сохранить бэкап не стирайте суперблок (диск), вытаскивайте диски..\n$LS Вместе со стиранием superblock, будет перегенерация таблиц GTP и очистка данных на диске (форматирование).\n$LS Если массив не удалился - переждите пару минут, диски заняты (Device or resource busy).\n$LS Или пробуйте перезагрузку и/или закрыть все приложения использующие массив (Dolphin итп).\n$LE"
    raidlist raidremove
  else
    echo -e "$LN$LS $2"
    R=$(ex "$2" 0 ' : ')
    echo 'idle' | sudo tee "/sys/block/$R/md/sync_action"
    fullumount "$R"
    confdel "$R"
    A=($(ex "$2" 0 '  (' 0)) # md2 : active raid1 sdc[1] sdb[0](F) sda3[0] sdb3[1](F)  (size...
    for ((I=4; I<${#A[*]}; I++)); do
      S=$(ex "${A[$I]}" 0 '[')
      fullumount "$S"
      # sudo mdadm "/dev/$R" -f "/dev/$S"        при сборке получим md:  kicking non-fresh sdc1 from array!
      # sudo mdadm "/dev/$R" -r "/dev/$S"
    done
    sudo mdadm -S "/dev/$R" # (-S или --stop)  справляется с бизи busy
    sudo dmsetup remove -f "/dev/$R" # Удаляем массив из Linux
    mdstat
    for ((I=4; I<${#A[*]}; I++)); do
      S=$(ex "${A[$I]}" 0 '[') # sdb3  [1](F)
      S=$(rtrim "$S" '[0-9]') # S=$(echo "$S" | sed 's/[0-9]*$//g')
      if ! [[ $MDUSED =~ " $S" ]]; then
        T=$(lsblk "/dev/$S" -ndo SIZE,SERIAL,MODEL,REV)
        echo $LE; MO=('Далее' "Стереть диск /dev/$S $T"); menu
        while :; do Y=$(waitkey)
          if [[ $Y == 0 ]]; then break
          elif [[ $Y == 1 ]]; then break
          elif [[ $Y == 2 ]]; then hands; diskclear "$S"; break; fi
        done
      fi
    done
    # Борьба с возможной ошибкой Device or resource busy
    sleep 3 # Задержка "на подумать" mdadm
    fullumount "$R" > /dev/null 2>&1
    sudo mdadm -S "/dev/$R" > /dev/null 2>&1
    sudo dmsetup remove -f "/dev/$R" > /dev/null 2>&1
    # sudo update-initramfs -u
    anykey 9
  fi
}
showdisk(){
  local A=$(sudo sgdisk -p "/dev/$1") L='' M=0
  while read L; do
    [[ $L =~ 'Number' ]] && M=1
    if (( M == 1 )) || [[ $L =~ 'Disk /dev/' || $L =~ 'Total free' ]]; then echo "$L"; fi
  done <<< "$(echo -e "$A")"
}
raidadd(){
  local A='' B='' C='' D=() F='' K='' L='' N='' I=0 J=0 Y=0 R='' ID=0 RS='' RD=0 RH=0 RN='' RNS='' S='' X=''
  head 'Создать RAID массив' 1
  echo -e "$LS$BG Существующие/работающие массивы: $BN"
  raidlist 1 1
  echo -e "$LN\n$LS Введите название нового массива md0, md1 итд. (цифру 0 - 999999) $LS $LS $LS $LS $LS $LS $LS $LS $LS $LS $LS  Выйти (Ctrl+C)"
  while :; do R=$(waitkey 'md')
    B=$(int "$R")
    if [[ $MDUSED =~ " md$B :" ]]; then err "Имя массива md$B занято!"
    else ID=$B; break; fi
  done
  echo -e "$LS Выбрано:$BG md$ID $BN\n$LN$LS Укажите желаемую RAID схему: $LS $LS $LS $LS $LS {допустимое количество вышедших из строя дисков с сохранением данных}\n$LE"
  MO=(
  "$BG raid 1  $BN 2 диска {1} и более {N-1}. Зеркальный дисковый массив (mirroring), данные пишутся параллельно. (Для дома - самое то)"
  "$BG raid 10 $BN 4 диска и более (чётное) {N/2}. 2 диска с чередованием и 2 диска зеркально им. Более производительная схема. (Она же RAID 1+0)"
  "$BG raid 4  $BN 3 диска и более {1}. Данные пишутся на диски поблочно + дополнительный диск для хранения информации о чётности данных блоков."
  "$BG raid 5  $BN 3 диска и более {1}. Запись идёт поочерёдно, объём на 1 диск меньше."
  "$BG raid 6  $BN 4 диска и более {2}. Вариант RAID 5 с записью контрольных сумм на 2 диска. Объём на 2 диска меньше."
  "$BG linear  $BN 2 диска и более {0}. Объединение дисков в один раздел. (JBOD)"
  "$BG raid 0  $BN 2 и более диска {0}. Массив повышенной производительности с чередованием, без отказоустойчивости. (striping)"
  ) # [multipath] не raid-массив, позволяющий создавать разные псевдо-дисковые устройства для одного физического диска
  menu 2; RS=''
  while :; do L=$(waitkey 'Введите номер пункта RAID схемы: ')
    B=$(int "$L");
    if [[ $L == 0 ]]; then exit 0
    elif [[ $L == 1 ]]; then STAGE=1; return 0
    elif [[ $B == 2 ]]; then RS='raid1'; MO=(2 3 4 5)
    elif [[ $B == 3 ]]; then RS='raid10'; MO=(4 6 8 10 12 14 16)
    elif [[ $B == 4 ]]; then RS='raid4'; MO=(3 4 5 6 7 8 9)
    elif [[ $B == 5 ]]; then RS='raid5'; MO=(3 4 5 6 7 8 9)
    elif [[ $B == 6 ]]; then RS='raid6'; MO=(4 5 6 7 8 9 10)
    elif [[ $B == 7 ]]; then RS='linear'; MO=(2 3 4 5 6 7 8 9 10)
    elif [[ $B == 8 ]]; then RS='raid0'; MO=(2 3 4 5 6 7 8 9 10); fi
    if [[ -n "$RS" ]]; then break; fi
  done
  echo -e "$LS Выбрано:$BG md$ID ($RS) $BN\n$LE\n$LS Укажите количество дисков в RAID:$LS $LS $LS $LS (дополнительные (HotSpare) на горячую замену будут следующим пунктом)\n$LE"
  menu 2
  while :; do L=$(waitkey 'Введите номер пункта количества дисков: ')
    if [[ $L == 0 ]]; then exit 0
    elif [[ $L == 1 ]]; then STAGE=1; return 0
    else B=$(optget "$L");
      if [[ -n "$B" ]]; then RD="$B"; break; fi
    fi
  done
  if [[ $RS == 'linear' || $RS == 'raid0' ]]; then RH=0
  else
    echo -e "$LS Выбрано:$BG md$ID ($RS) $BN Дисков:$BG $RD $BN\n$LN$LS Укажите количество HotSpare дисков: $LS $LS $LS $LS $LS (Не обязательно,\n$LS $LS такие диски работают в режиме простоя до момента поломки одного из дисков и автоматически замещают вышедший из строя.\n$LS $LS Это актуально для серверов, для дома проще и дешевле поменять по факту поломки.)\n$LE"
    MO=(0 1 2 3 4)
    menu 2
    while :; do L=$(waitkey 'Введите номер пункта HotSpare дисков: ')
      if [[ $L == 0 ]]; then exit 0
      elif [[ $L == 1 ]]; then STAGE=1; return 0
      else B=$(optget "$L");
        if [[ -n "$B" ]]; then RH="$B"; break; fi
      fi
    done
  fi
  F=''; findisks
  echo -e "$LS Выбрано:$BG md$ID ($RS) $BN Дисков:$BG $RD $BN Запасных(HotSpare):$BG $RH $BN \n$LN$LS Устройства в системе:\n$LN$$LS DISKSH"
  for ((I=0; I<${#DISKS[*]}; I++)); do echo -e "$LS ${DISKS[$I]}"; done
  echo -e "$LN$LS Укажите диск/и:     (разделы создадим или укажем далее)\n$LS (missing - пропустить, диска нет.. Участники работающих массивом пропускаются.\n$LE"
  for ((I=1; I <= $RD; I++)); do
    echo "$LS$LS$DISKSH"; MO=('missing')
    for ((J=0; J<${#DISKS[*]}; J++)); do L=${DISKS[$J]};
      if [[ $L =~ 'disk' ]]; then N=$(ex "$L" 1)
        ! [[ "$MDUSED $RN " =~ " $N " ]] && opt "$L"
      fi
    done
    menu 1
    while :; do R=$(waitkey "Введите $I диск: ")
      [[ $R == 0 ]] && exit 0
      N=$(optget "$R");
      if [[ -n $N ]]; then S="$N"
        if [[ $S != 'missing' ]]; then S=$(ex "$S" 1)
          if [[ -z $F ]]; then F="$S"; fi
        fi
        RN+="$S "; echo -e "$LN$LS Выбраны:$BG $RN $BN\n$LE"; break
      fi
    done
  done
  if [[ $RH != 0 ]]; then
    for ((I=1; I<=RH; I++)); do
      echo "$LS$LS$DISKSH"; MO=('missing')
      for ((J=0; J<${#DISKS[*]}; J++)); do L=${DISKS[$J]};
        if [[ $L =~ 'disk' ]]; then N=$(ex "$L" 1)
          ! [[ "$MDUSED $RN $RNS" =~ " $N " ]] && opt "$L"
        fi
      done
      menu 1
      while :; do R=$(waitkey "Введите HotSpare $I диск: ")
        [[ $R == 0 ]] && exit 0
        N=$(optget "$R");
        if [[ -n $N ]]; then S="$N"
          if [[ $S != 'missing' ]]; then S=$(ex "$S" 1)
            if [[ -z $F ]]; then F="$S"; fi
          fi
          RNS+="$S "; echo -e "$LN$LS Выбраны:$BG $RN $BN Запасные:$BG $RNS $BN\n$LE"; break
        fi
      done
    done
  fi
  if [[ -z "$F" ]]; then err 'Не указан ни один диск!'; anykey; return 0; fi
  MO=('Разметить диски (будет уничтожена разметка дисков)' 'Указать разделы (если раздел единственный - подцепит на автомате)')
  menu 1
  while :; do R=$(waitkey)
    if [[ $R == 0 ]]; then exit 0
    elif [[ $R == 1 ]]; then X=1; break
    elif [[ $R == 2 ]]; then X=0; break; fi
  done
  if [[ $X == 1 ]]; then
    hands 'Произойдёт стирание дисков. '
    I=0; B=($RN$RNS)
    for L in ${B[*]}; do [[ $L != 'missing' ]] && diskclear "$L"; done
    head 'Разметить диски GPT -> Укажите отступ в конце диска' 1
    echo -e "$LS У разных дисков размер может отличаться, поэтому необходим отступ для RAID в конце:$BG SSD/NVMe -10%/-20%$BN; HDD -100Мб.\n$LS Твёрдотельным SSD/NVMe дискам неразмеченная область продлевает жизнь и влияет на производительность увеличивая IO.\n$LS При кратных двойке (полных) объёмах$BG 128/256/512/1024ГБ $BNрезерва нет - оставляем$BG 20% $BNместа не размеченным,\n$LS у не полных$BG 120/240/480/960ГБ $BNприсутствует резервная область недоступная пользователю - оставляем$BG 10%$BN.\n$LN$LS Выбраны:$BG $RN $BN\n$LE"
    MO=('SSD/NVMe: -10%' 'SSD/NVMe: -20%' 'HDD: -100Мб' 'Разметить по своему')
    menu 1
    X=$(lsblk -ndbo SIZE "/dev/$F") # 120034123776 = 111.8G (120G)
    X=$((X*10/100))             # 10% 12003412377  =  11.2G
    # Выравнивание chunk 512К. В RAID5 итп. ещё делим и домножаем на количество дисков.
    X=$((X/1024)) # 11722082 = 11447M (11447.3457031)
    X=$((X/512))  # 22894K chunk (22894.6914062)
    E=$((X*512))
    while :; do R=$(waitkey)
        if [[ $R == 1 ]]; then X='-'$E'K'; sgd "$F" 1 "$X"; break
      elif [[ $R == 2 ]]; then X='-'$((E*2))'K'; sgd "$F" 1 "$X"; break
      elif [[ $R == 3 ]]; then sgd "$F" 1 '-102400K'; break
      elif [[ $R == 4 ]]; then I=1; break; fi
    done
    if [[ $I == 1 ]]; then
      MO=(); menu 1
      while :; do R=$(waitkey 'Введите количество разделов на диске: ')
        [[ $R == 0 ]] && exit 0; B=$(int "$R"); (( R != 0 )) && break
      done
      echo -e "$LN$LS Разметка дисков в формате: начало_раздела конец_раздела код(тип)_файловой_системы. (через пробел, 0 означает на автомате взять начало, конец.. )\n$LS начало_раздела(First sector) {+-}size{KMGTP}\n$LS конец_раздела(Last sector) {+-}size{KMGTP}\n$LS код(тип)_файловой_системы: 0 = 8300 Linux filesystem; 1 = EF00 EFI System Partition; 2 = 8200 Linux swap; 3 = FD00 Linux RAID; 4 = EF02 BIOS boot partition (bios_grub);\n$LS или вводите код - посмотреть можно введя 9 (sudo sgdisk -L)\n$LS Примеры: 0 0 0; 0 +360M 1; 0 +30G 0; 0 -100M 0$LS Можно пропускать 1 и последнее значение: 0 +30G 0 = +30G 0 = +30G
      \n$LE"
      for ((I=1; I<=B; I++)); do
        showdisk "$F"; MO=(); menu 1
        while :; do R=$(waitkey "Разметка $I раздела из $B: ")
          if [[ $R == 0 ]]; then exit 0
          elif [[ $R == 9 ]]; then sudo sgdisk -L
          else
            A=($R); C=${#A[*]};
            if (( C == 1 )); then L=0; X="${A[0]}"; N='8300'
            elif (( C == 2 )); then L=0; X="${A[0]}"; N="${A[1]}"
            else L="${A[0]}"; X="${A[1]}"; N="${A[2]}"; fi
            if [[ $N == 0 ]]; then N='8300'
            elif [[ $N == 1 ]]; then N='EF00'
            elif [[ $N == 2 ]]; then N='8200'
            elif [[ $N == 3 ]]; then N='FD00'
            elif [[ $N == 4 ]]; then N='EF02'; fi
            sudo sgdisk "/dev/$F" -n "$I":"$L":"$X" -t "$I":"$N"
            echo $LE
            break
          fi
        done
      done
    fi
    showdisk "$F"
    B=($RN)
    for L in ${B[*]}; do
      if [[ $L != 'missing' && $L != $F ]]; then
        echo -e "$LN$(center "Копирую разметку $F в $L")\n$LE"
        diskclear "$L"
        sudo sgdisk "/dev/$F" -R "/dev/$L"
        sudo sgdisk -G "/dev/$L"
      fi
    done
  fi
  D=$(lsblk -I 8 -no TYPE,NAME)
  X=''; A=($RN) # echo "-$RN-" # -sdb sdc - || -missing sdb -; $RD == 2
  for ((I=0; I<RD; I++)); do N=${A[$I]}
    if [[ $N == 'missing' ]]; then X+=" $N"; else J=0; MO=()
      while read L; do B=$(ex "$L" 1)
        if [[ $L =~ 'part' && $B =~ $N ]]; then B=$(ex "$B" 1 '─') # ├─sdb1 =~ sdb
          if ! [[ "$MDUSED" =~ " $B " ]]; then MO[$J]=" /dev/$B"; ((J++)); fi
        fi
      done <<< "$(echo -e "$D")"
      if (( J == 1 )); then X+="${MO[0]}"; else
        echo -e "$LN$LS Укажите раздел для RAID массива$BG md$ID ($RS) $BNна диске$BG $N$BN:"
        menu
        while :; do L=$(waitkey)
          C=$(optget "$L");
          if [[ -n $C ]]; then X+="$C"; echo -e "$LN$LS Выбраны:$X"; break; fi
        done
      fi
    fi
  done
  S=''
  if [[ $RH != 0 ]]; then A=($RNS)
    for ((I=0; I < $RH; I++)); do N=${A[$I]}
      if [[ $N == 'missing' ]]; then S+=" $N"; else J=0; MO=()
        while read L; do B=$(ex "$L" 1)
          if [[ $L =~ 'part' && $B =~ $N ]]; then B=$(ex "$B" 1 '─')
            if ! [[ "$MDUSED" =~ " $B " ]]; then MO[$J]=" /dev/$B"; ((J++)); fi
          fi
        done <<< "$(echo -e "$D")"
        if (( J == 1 )); then S+="${MO[0]}"; else
          echo -e "$LN$LS Укажите раздел для RAID массива$BG md$ID ($RS) $BNна запасном (HotSpare) диске$BG $N$BN:"
          menu
          while :; do L=$(waitkey)
            C=$(optget "$L");
            if [[ -n $C ]]; then S+="$C"; echo -e "$LN$LS Выбраны:$S"; break; fi
          done
        fi
      fi
    done
  fi
  [[ -n $S ]] && S=" -x $RH$S"
  # --level=-l : raid level: 0,1,4,5,6,10,linear,multipath and synonyms
  C=${RS//[^0-9]} # To int $(int "$RS") вернёт 0
  [[ -z $C ]] && C='linear'
  confdel "md$ID"
  # sudo dmraid -E -r /dev/sdd # & reboot   # yum erase dmraid
  # sudo dmsetup remove_all # Удаляем массив из Linux. mdadm: Cannot open /dev/sdb1: Device or resource busy
  # --assume-clean отменить синхронизацию
  S="sudo mdadm -Cv /dev/md$ID -l $C -n $RD$X$S"
  # mdadm -Cv /dev/md0 -l1 -n2 /dev/sd[ab]2 -x1 /dev/sdf1
  # mdadm --create --verbose /dev/md0 --level=1 --raid-devices=2 /dev/sda2 /dev/sdb2 --spare-devices=1 /dev/sdf1
  echo "$S"
  yes | $($S)
  confadd "$ID"
  sudo update-initramfs -u
  echo -e "$LN$LS Не забудьте отформатировать массив после синхронизации (по окончанию создания массивов - выйдете в основное меню)"
  anykey 9
}
raidformat(){
  local R=''
  if [[ -z $1 ]]; then
    head 'Отформатировать RAID массив (будет отмонтирован на время форматирования)' 1
    raidlist raidformat
  else
    R=$(ex "$2" 0 ' : ')
    fullumount "$R"
    frmt "$R"
    sudo partprobe "/dev/$R" # Оповещаем ядро об изменениях таблицы разделов raid.
    sudo mount -a
    echo -e "$LN$LS Осталось примонтировать массив (4-й пункт главного меню)"
    anykey 9
  fi
}
raidcheck(){
  local R='' A='' B='' I=0 J=0 S=''
  head 'Запустить проверку массива (будет отмонтирован на время проверки)' 1
  if [[ -z "$1" ]]; then raidlist raidcheck
  else
    R=$(ex "$2" 0 ' : ')
    if [[ -n $R && -e "/dev/$R" ]]; then clear
      sudo umount -lfv "/dev/$R"
      echo "$LS Проверка /dev/$R"
      sudo e2fsck -fpv "/dev/$R"
      sudo mount -a
    else err "RAID массива (/dev/$R) не найдено."; fi
    anykey 9
  fi
}
testspeed(){
  local I='' M=''
  head 'Тест скорости записи и чтения RAID массива' 1
  if [[ -z $1 ]]; then raidlist testspeed
  else
    I=$(ex "$2" 0 ' : ')
    M=$(lsblk "/dev/$I" -ndo MOUNTPOINT)
    if [[ -z $M ]]; then echo 'RAID массив не примонтирован к системе'; anykey
    else
      head 'Минимальная скорость записи (64 раза по 512 байт)' 1
      sudo dd if=/dev/zero of=$M/speedtest.bin bs=512 count=64 oflag=dsync
      head 'Скорость записи (файл 256Мб)'
      sudo dd if=/dev/zero of=$M/speedtest.bin bs=256M count=1 oflag=dsync
      head 'Скорость чтения (2048 раз по 512 байт)'
      sudo dd of=/dev/null if=$M/speedtest.bin bs=512 count=2048 oflag=dsync
      sudo rm -f $M/speedtest.bin
      anykey 9
    fi
  fi
}
syncspeedset(){
  local A=${1-} B=0
  if ((A == 2)); then B=153600
  elif ((A == 3)); then B=307200
  elif ((A == 4)); then B=614400
  elif ((A == 5)); then B=1258291
  elif ((A == 6)); then B=3355443
  else
    B=$(int "$A")
    A=${A,,} # В нижний регистр, ^^ в верхний
    if [[ $A =~ 'm' || $A =~ 'м' ]]; then B=$((B * 1024))
    elif [[ $A =~ 'g' || $A =~ 'г' ]]; then B=$((B * 1048576)); fi
  fi
  if ((B != 0)); then
    A=$((B / 1024))
    echo -e "$LN$(center "Установлена скорость синхронизации RAID $A МБ, КБ:")"
    echo "$B" | sudo tee /proc/sys/dev/raid/speed_limit_min # = 1000 в КБ
    echo "$B" | sudo tee /proc/sys/dev/raid/speed_limit_max # = 200000 в КБ
  fi
}
syncspeed(){
  local R=''
  MO=('SATA = 150 МБ/с  = 153600 КБ'
      'SATA2 = 300 МБ/с = 307200 КБ'
      'SATA3 = 600 МБ/с = 614400 КБ'
      'SAS 12G = 1,2 ГБ/с = 1258291 КБ (Serial Attached SCSI)'
      'NVMe = 3.2 ГБ/с = 3355443 КБ (PCI Express 3.0 x4)')
  clear; echo -e "$LN$LS Выберете ограничение скорости или введите своё значение (в КБ)\n$LS Можно указать в МБ = 100m или 100м или 1ГБ = 1g или 1г (регистр не важен)\n$LS Пример: 1 Гб * 1024 = 1024 МБ * 1024 = 1048576 КБ\n$LE"
  menu 2; R=$(waitkey 'Ограничить скорость синхронизации RAID: ')
  if [[ $R == 0 ]]; then exit 0
  elif [[ $R == 1 ]]; then STAGE=1
  else
    syncspeedset "$R"
    anykey 1
  fi
}
raidsync(){
  local R='' T="$LS Посмотреть нашлись ли какие-то ошибки в процессе проверки:\n$LS cat /sys/block/md?/md/mismatch_cnt"
  head 'Запустить синхронизацию массива'
  echo -e "$T"
  if [[ -z $1 ]]; then raidlist raidsync
  else clear
    R=$(ex "$2" 0 ' : ')
    echo -e "$T"
    echo 'check' | sudo tee "/sys/block/$R/md/sync_action" # resync repair idle
    anykey 9
  fi
}
raidrename(){
  local ID='' B='' C='' L='' O='' R='' S='' I=0
  head 'Переименовать RAID массив' 1
  if [[ -z $1 ]]; then raidlist raidrename
  else
    mdstat
    O=$(ex "$2" 0 ' : ')
    echo -e "$LS Если mdadm напишет: Cannot get exclusive access to ... Perhaps a running process, mounted filesystem or active volume group?\n$LS --> Закройте все директории (менеджер файлов) и приложения работающие с массивом!\n$LN$LS$BG Существующие/работающие массивы: $BN"
    raidlist 1 1
    if [[ -z $O ]]; then err 'Не выбран массив!'; anykey; return 1; fi
    echo -e "$LN$LS Выбран массив:$BG $O $BN\n$LN\n$LS Введите название нового массива md0, md1 итд. (цифру 0 - 999999) $LS $LS $LS $LS $LS $LS $LS $LS $LS $LS $LS  Выйти (Ctrl+C)"
    while :; do R=$(waitkey 'md')
      B=$(int "$R")
      if [[ $MDUSED =~ " md$B :" ]]; then err "Имя массива md$B занято!"
      else ID=$B; break; fi
    done
    while read L; do
      if [[ $L =~ "$O : " ]]; then
        echo "$L" # md2 : active raid1 sdc[1] sdb[0](F) sda3[0] sdb3[1](F)  (size...
        B=($(ex "$L" 0 '  (' 0))
        for ((I=4; I<${#B[*]}; I++)); do S+=' /dev/'$(ex "${B[$I]}" 0 '['); done
        break
      fi
    done <<< "$(echo -e "$MDS")"
    fullumount "$O"
    sudo mdadm -S "/dev/$O" # --stop
    sudo mdadm -r "/dev/$O" # --remove
    confdelm "$O" # Удалить старые записи - md2  cat /etc/mdadm/mdadm.conf
    sleep 3 # Задержка "на подумать" mdadm
    sudo sed -i "s/\/dev\/$O/\/dev\/md$ID/g" /etc/fstab # /a was on /dev/md1
    #sudo mdadm -A /dev/md$ID $S --update=name # mdadm --assemble /dev/md3 /dev/sd[abcdefghijk]3 --update=name
    #sudo mdadm -A --name=$ID --update=name /dev/md$ID $S
    S="sudo mdadm -A --name=$ID --update=name /dev/md$ID$S"
    echo "$LS $S"
    $($S)
    # udo mdadm --assemble --update=name --name=2 /dev/md2 /dev/sdh /dev/sdi              sudo mdadm -Esv
    #–update=name – We are going to set the name of the array in the array’s superblock. Other fields exist, but name is the one that changes the name.
    #–name=2 – The name to use. 2, in this example.
    #/dev/md2 – The array’s device name is to be /dev/md2.
    #/dev/sdh and /dev/sdi – The member drives. With more drives in the system, expect higher letters.
    confadd "$ID"
    sudo mount -a
    sudo mdadm -Asv
    sudo update-initramfs -u
    anykey 9
  fi
}
partmount(){
  local I=0 L='' R='' N='' S='' B='' U=''
  head 'Создать точку(директорию) монтирования и примонтировать раздел диска' 1
  echo -e "$LS Директория будет создана, если её нет. Произойдёт отмонтирование директории и выбранного раздела диска, и примонтирование к новой.\n$LE"
  findisks
  echo -e "$LS Устройства в системе:\n$LN$LS $DISKSH"
  for ((I=0; I<${#DISKS[*]}; I++)); do echo -e "$LS ${DISKS[$I]}"; done
  echo -e "$LN$LS Укажите раздел диска:\n$LE\n$LS$LS$LS $DISKSH"; MO=()
  for ((J=0; J<${#DISKS[*]}; J++)); do L=${DISKS[$J]};
    if [[ $L =~ 'part' ]]; then N=$(ex "$L" 1); N=$(ex "$N" 1 '─')
      ! [[ "$MDUSED $RN " =~ " $N " ]] && opt "$L"
    fi
  done
  menu 1
  while :; do R=$(waitkey "Введите номер раздела: ")
    if [[ $R == 0 ]]; then exit 0
    else N=$(optget "$R");
      if [[ -n $N ]]; then S="$N"; S=$(ex "$S" 1); S=$(ex "$S" 1 '─'); break; fi
    fi
  done
  U=$(uuid "$S")
  echo -e "$LN$LS Выбран:$BG $S $BN $LS $LS $LS Введите название директории или укажите номер из предложенных: \n$LE";
  MO=('/a' '/files' '/games')
  menu 2
  while :; do R=$(waitkey 'Введите название директории /')
    if [[ $R == 0 ]]; then exit 0
    elif [[ $R == 1 ]]; then STAGE=1; return 0
    elif [[ -n $R ]]; then
      if [[ $R == 2 ]]; then B='/a';
      elif [[ $R == 3 ]]; then B='/files';
      elif [[ $R == 4 ]]; then B='/games';
      else B="/$R"; fi
      echo -e "$LS Создаём директорию$BG $B $BN"; hands; clear
      confdel "$S" 1 # Если примонтировано удалить запись
      sudo mkdir -pvm 755 "$B" # sudo chmod -v 755 "$B"
      sudo umount -lfv "$B"
      fullumount "$S"
      grep -v "$B  ext4" /etc/fstab | sudo tee /etc/fstab > /dev/null 2>&1
      # Устройство точка_монтирования файловая_система опции резерв{0,1} проверка{0,1,2}
      if [[ -z $U ]]; then
        echo -e "# $B was on /dev/$S \n/dev/$1  $B  ext4  defaults,noatime,nodiratime,nofail 0 0" | sudo tee -a /etc/fstab
        echo "$LS UUID массива /dev/$S не найден :( Устройство примонтировано по имени."
      else echo -e "# $B was on /dev/$S \nUUID=$U  $B  ext4  defaults,noatime,nodiratime,nofail 0 0" | sudo tee -a /etc/fstab; fi
      sleep 1
      sudo mount -a
      sleep 2
      sudo chown -R $USER:$USER "$B"
      anykey 10
      break
    fi
  done
}
delmount(){
  local A=$(cat /etc/fstab) L='' R=''
  head 'Удалить точку монтирования из /etc/fstab' 1
  MO=()
  while read L; do
    ! [[ -z $L || $L =~ '#' ]] && opt "$L"
  done <<< "$(echo -e "$A")"
  menu 2
  while :; do R=$(waitkey)
    if [[ $R == 0 ]]; then exit 0
    elif [[ $R == 1 ]]; then STAGE=1; return 0
    else
      L=$(optget "$R");
      if [[ -n $L ]]; then
        L=$(ex "$L" 1)
        sudo umount -lfv "$L"
        grep -v " $L " /etc/fstab | sudo tee /etc/fstab
        anykey 10
        break
      fi
    fi
  done
}
disknil(){
  if [[ -z $1 ]]; then
    head 'Стереть диск' 1
    center 'Участники массивов пропускаются в целях безопасности.' 1
    diskslist disknil 2
  else
    echo -e "$LN$LS Стираем: /dev/$BG$2$BN"
    hands; diskclear "$2"; anykey
  fi
}
raidstop(){
  local R=''
  head 'Остановить RAID' 1
  if [[ -z $1 ]]; then raidlist raidstop
  else
    R=$(ex "$2" 0 ' : ')
    if [[ -n $R && -e "/dev/$R" ]]; then clear
      sudo umount -lfv "/dev/$R"
      sudo mdadm -S "/dev/$R"
    else err "RAID массива (/dev/$R) не найдено."; fi
    anykey 9
  fi
}
raidstart(){
  local R='' F=0 A='' M='/etc/mdadm/mdadm.conf' L='' Y=''
  MO=('Запустить все')
  if [[ -e $M ]]; then
    A=$(cat $M)
    head 'Запустить RAID' 1
    while read L; do
      if [[ $L =~ 'ARRAY' ]]; then
        if ((F == 0)); then echo -e "$LS$BG Предопределены массивы в $M: $BN"; F=1; fi
        opt "$L"
      fi
    done <<< "$(echo -e "$A")"
    ((F == 1)) && echo "$LE"
  fi
  menu 2
  while :; do R=$(waitkey 'Введите номер диска: ')
    if [[ $R == 0 ]]; then exit 0
    elif [[ $R == 1 ]]; then STAGE=9; break
    elif [[ $R == 2 ]]; then sudo mdadm -Asv; anykey 9; break
    else
      L=$(optget "$R");
      L=$(ex "$L" 1)
      L=$(ex "$L" 1 '/dev/')
      L=${L/\//}
      if [[ -n $L ]]; then
        echo -e "$LS try: sudo mdadm -A /dev/$L"
        sudo mdadm -A "/dev/$L" # Это работает только с предопределёнными массивами в конфиге.
        echo -e "$LS try: sudo mount -a"
        sudo mount -a
      else err "RAID массива (/dev/$L) не найдено."; fi
      anykey 9
      break
    fi
  done
}
diskparted(){
  local A='' B=0 E=1 S=0 SE='' X=1 X2=1 O=() L='' N='' RN='' C='' R='' I=0 F='' D=()
  mdstat; head 'Разметить диски' 1; findisks
  echo -e "$LS Устройства в системе:\n$LN$LS $DISKSH"
  for ((I=0; I<${#DISKS[*]}; I++)); do echo -e "$LS ${DISKS[$I]}"; done
  echo -e "$LN$LS Укажите диск/и: (участники работающих массивов пропускаются, разметка клонируется после первого размеченного)\n$LE"
  while [[ $E == 1 ]]; do
    MO=('Завершить (перейти к выбору разметки)')
    for ((I=0; I<${#DISKS[*]}; I++)); do L=${DISKS[$I]};
      if [[ $L =~ 'disk' ]]; then N=$(ex "$L" 1)
        ! [[ "$MDUSED $RN " =~ " $N " ]] && opt "$L"
      fi
    done
    menu 1
    while :; do R=$(waitkey "Введите $X диск: ")
      if [[ $R == 0 ]]; then exit 0
      elif [[ $R == 1 ]]; then E=0
      else N=$(optget "$R");
        if [[ -n $N ]]; then
          ((X++)); N=$(ex "$N" 1); [[ -z $F ]] && F=$N
          RN+="$N "; echo -e "$LN$LS Выбраны:$BG $RN $BN\n$LE"
        fi
      fi
      break
    done
  done
  if [[ -z $F ]]; then err 'Не указан ни один диск!'; anykey; return 0; fi
  head 'Разметить диски -> Укажите отступ в конце диска' 1
  echo -e "$LS У разных дисков размер может отличаться, поэтому необходим отступ для RAID в конце:$BG SSD/NVMe -10%/-20%$BN; HDD -100Мб.\n$LS Твёрдотельным SSD/NVMe дискам неразмеченная область продлевает жизнь и влияет на производительность увеличивая IO.\n$LS При кратных двойке (полных) объёмах$BG 128/256/512/1024ГБ $BNрезерва нет - оставляем$BG 20% $BNместа не размеченным,\n$LS у не полных$BG 120/240/480/960ГБ $BNприсутствует резервная область недоступная пользователю - оставляем$BG 10%$BN.\n$LN$LS Выбраны:$BG $RN $BN\n$LE"
  MO=('SSD/NVMe: -10%' 'SSD/NVMe: -20%' 'HDD: -100Мб')
  menu 1
  while :; do R=$(waitkey); S=$(int "$R");
    [[ $R == 0 ]] && exit 0; ((S > 0 && S < 4)) && break
  done
  head 'Разметить диски GPT -> Выбор схемы' 1; SE=$(optget "$S")
  echo -e "$LS Раздел EFI /boot/efi загрузчика UEFI и MSDOS - BIOS boot partition (bios_grub) не RAID-ить!\n$LS EFI копируется целиком с помощь dd на все диски в случае RAID, а при /bios_grub устанавливается на все диски.\n$LS MSDOS для совсем старого железа.\n$LN$LS Выбраны:$BG $RN $BN  $LS Отступ в конце:$BG $SE $BN\n$LE"
  MO=('UEFI: 1=/boot/efi 360Мб [fat32]. 2=/ 30Гб [ext4]. 3=/home [ext4]'
      'UEFI: 1=/boot/efi 360Мб [fat32]. 2=/ 50Гб [ext4]. 3=/home [ext4]\n'
      'UEFI: 1=/boot/efi 360Мб [fat32]. 2=/ [ext4]        (/home на отдельном RAID)'
      'UEFI: 1=Весь диск [ext4]\n'
      'MSDOS: 1=/bios_grub 1Мб. 2=/boot 1Гб [ext4]. 3=/ 30Гб [ext4]. 4=/home [ext4]'
      'MSDOS: 1=/bios_grub 1Мб. 2=/boot 1Гб [ext4]. 3=/ 50Гб [ext4]. 4=/home [ext4]'
      'MSDOS: 1=/bios_grub 1Мб. 2=/boot 1Гб [ext4]. 3=/ [ext4]        (/home на отдельном RAID)\n'
      'Разметить по своему (без форматирования (сотрёт диск/и как и остальные пункты))')
  X=$(lsblk -ndbo SIZE "/dev/$F") # 120034123776 = 111.8G (120G)
  X=$((X*10/100))             # 10% 12003412377  =  11.2G
  # Выравнивание chunk 512К. В RAID5 итп. ещё делим и домножаем на количество дисков.
  X=$((X/1024)) # 11722082 = 11447M (11447.3457031)
  X=$((X/512))  # 22894K chunk (22894.6914062)
  E=$((X*512))
  if ((S == 1)); then X='-'$E'K' # 10%
  elif ((S == 2)); then X='-'$((E*2))'K' # 20%
  elif ((S == 3)); then X='-102400K'; fi # -100Мб
  I=0; menu 1
  while :; do R=$(waitkey)
    if [[ $R == 0 ]]; then exit 0
    elif [[ $R == 8 ]]; then I=1; break
    else B=$(int "$R")
      if ((B > 0 && B < 9)); then
        hands; clear; diskclear "$F"
        if ((B == 1 || B == 5)); then S='+30G'
        elif ((B == 2 || B == 6)); then S='+50G'
        else S="$X"; fi
        if ((B > 0 && B < 4)); then sgdefi "$F"; sgd "$F" 2 "$S"; frmt "$F1" 1; frmt "$F2"; fi
          if ((B > 0 && B < 3)); then sgd "$F" 3 "$X"; frmt "$F3"; fi
        if ((B == 4)); then sgd "$F" 1 "$X"; frmt "$F1"; fi
        if ((B > 4 && B < 8)); then sgdgrab "$F"; sgdboot "$F"; sgd "$F" 3 "$S"; frmt "$F2"; frmt "$F3"; fi
          if ((B > 4 && B < 7)); then sgd "$F" 4 "$X"; frmt "$F4"; fi
        break
      fi
    fi
  done
  if [[ $I == 1 ]]; then
    head 'Разметить диски по своему (без форматирования (сотрёт диск/и))' 1
    MO=(); menu 1
    while :; do R=$(waitkey 'Введите количество разделов на диске: ')
      [[ $R == 0 ]] && exit 0; B=$(int "$R"); (( R != 0 )) && break
    done
    hands; clear; diskclear "$F"
    echo -e "$LN$LS Разметка дисков в формате: начало_раздела конец_раздела код(тип)_файловой_системы. (через пробел, 0 означает на автомате взять начало, конец.. )\n$LS начало_раздела(First sector) {+-}size{KMGTP}\n$LS конец_раздела(Last sector) {+-}size{KMGTP}\n$LS код(тип)_файловой_системы: 0 = 8300 Linux filesystem; 1 = EF00 EFI System Partition; 2 = 8200 Linux swap; 3 = FD00 Linux RAID; 4 = EF02 BIOS boot partition (bios_grub);\n$LS или вводите код - посмотреть можно введя 9 (sudo sgdisk -L)\n$LS Примеры: 0 0 0; 0 +360M 1; 0 +30G 0; 0 -100M 0$LS Можно пропускать 1 и последнее значение: 0 +30G 0 = +30G 0 = +30G
    \n$LE"
    for ((I=1; I<=$B; I++)); do
      showdisk "$F"; MO=(); menu 1
      while :; do R=$(waitkey "Разметка $I раздела из $B: ")
        if [[ $R == 0 ]]; then exit 0
        elif [[ $R == 9 ]]; then sudo sgdisk -L
        else
          A=($R); C=${#A[*]};
          if (( C == 1 )); then L=0; X="${A[0]}"; N='8300'
          elif (( C == 2 )); then L=0; X="${A[0]}"; N="${A[1]}"
          else L="${A[0]}"; X="${A[1]}"; N="${A[2]}"; fi
          if [[ $N == 0 ]]; then N='8300'
          elif [[ $N == 1 ]]; then N='EF00'
          elif [[ $N == 2 ]]; then N='8200'
          elif [[ $N == 3 ]]; then N='FD00'
          elif [[ $N == 4 ]]; then N='EF02'; fi
          sudo sgdisk "/dev/$F" -n "$I":"$L":"$X" -t "$I":"$N"
          echo $LE
          break
        fi
      done
    done
  fi
  showdisk "$F"
  B=($RN)
  for L in ${B[*]}; do
    if [[ $L != $F ]]; then
      echo -e "$LN$(center "Копирую разметку $F в $L")\n$LE"
      diskclear "$L"
      sudo sgdisk "/dev/$F" -R "/dev/$L"
      sudo sgdisk -G "/dev/$L"
    fi
  done
  echo -e "$LN$(center 'Завершено')"
  anykey
}
partformat(){
  local I=1 L='' R='' N='' S='' B='' U=''
  head 'Отформатировать раздел диска (будет отмонтирован на время форматирования)' 1
  findisks
  echo -e "$LS Устройства в системе:\n$LN$LS $DISKSH"
  for ((I=0; I<${#DISKS[*]}; I++)); do echo -e "$LS ${DISKS[$I]}"; done
  echo -e "$LN$LS Укажите раздел диска:\n$LE\n$LS$LS$LS $DISKSH"; MO=()
  for ((J=0; J<${#DISKS[*]}; J++)); do L=${DISKS[$J]};
    if [[ $L =~ 'part' ]]; then N=$(ex "$L" 1); N=$(ex "$N" 1 '─')
      ! [[ "$MDUSED $RN " =~ " $N " ]] && opt "$L"
    fi
  done
  menu 1
  while :; do R=$(waitkey "Введите номер раздела: ")
    if [[ $R == 0 ]]; then exit 0
    else N=$(optget "$R");
      if [[ -n $N ]]; then S="$N"; S=$(ex "$S" 1); S=$(ex "$S" 1 '─'); break; fi
    fi
  done
  fullumount "$S"
  frmt "$S"
  sudo partprobe "/dev/$S" # Оповещаем ядро об изменениях таблицы разделов.
  sudo mount -a
  anykey 10
}
swap(){
  local X=0 Z=0 R='' A=''
  head 'Создать/Удалить своп файл /swapfile (файл подкачки)' 1
  echo -e "$LS В Мб (free -m):"
  free -m
  if [[ -e /swapfile ]]; then
    X=$(find /swapfile -printf "%s")
    echo -e "$LN$LS Найден своп файл /swapfile ~"$(bytes $X)"\n$LN"
    MO=('Удалить'); menu 2
    while :; do R=$(waitkey)
      if [[ $R == 0 ]]; then exit 0
      elif [[ $R == 1 ]]; then STAGE=10; return 0
      elif [[ $R == 2 ]]; then
        sudo swapoff -a /swapfile
        sleep 3
        grep -ve "^$" -e "/swapfile" /etc/fstab | sudo tee /etc/fstab > /dev/null 2>&1
        sudo rm -f /swapfile
        anykey 10
      break; fi
    done
  else
    echo -e "$LN$LS Для режима сна ноутбука: размер памяти(ОЗУ/RAM) +2Гб\n$LS Для компьютера с большим количеством памяти: символически 2Гб\n$LS Для сервера обычно ставят равным или в 2 раза большим памяти.\n$LS$LS Рекомендации от специалистов из:\n$LS$LS$LS CentOS: ОЗУ + 2 Гб. Если менее 2 Гб - ОЗУ * 2.\n$LS$LS$LS Red Hat: 20% от ОЗУ (от 4 Гб)\n$LS$LS$LS Canonical: от квадратного корня из объема ОЗУ до удвоенного значения. Если менее 1 Гб - ОЗУ * 2.\n$LS На больших объёмах уменьшается, зависит от сервера конечно..\n$LS ОЗУ(Гб)=Swap(Гб): 6=2; 8=3; 12=3; 16=4; 24=5; 32=6; 64=8; 128=11\n$LE"
    MO=('Создать своп файл /swapfile')
    menu 2
    while :; do R=$(waitkey)
      if [[ $R == 0 ]]; then exit 0
      elif [[ $R == 1 ]]; then STAGE=10; return 0
      elif [[ $R == 2 ]]; then X=1; break; fi
    done
    if [[ $X == 1 ]]; then
      X=0;
      echo -e "$LN$LS Выберете размер /swapfile (по умолчанию в байтах)\n$LS Можно указать в МБ = 100m или 100м или 1ГБ = 1g или 1г (регистр не важен)\n$LS Пример: 1 Гб * 1024 = 1024 МБ * 1024 = 1048576 КБ * 1024 = 1073741824 Б"
      while :; do R=$(waitkey 'Введите размер /swapfile: ')
        if [[ -n $R ]]; then
          X=${R//[^0-9]} # To int
          A="${R,,}"
          if [[ $A =~ 'm' || $A =~ 'м' ]]; then Z='M'
          elif [[ $A =~ 'g' || $A =~ 'г' ]]; then Z='G'; fi
          sudo fallocate -l $X$Z /swapfile
          sudo chmod 600 /swapfile
          sudo mkswap /swapfile
          sudo swapon /swapfile
          [[ -z $(cat /etc/fstab | grep '/swapfile') ]] && echo '/swapfile    none    swap    sw    0    0' | sudo tee -a /etc/fstab > /dev/null 2>&1
          anykey 10
          break
        fi
      done
    fi
  fi
}
grub2(){
  local I=0 L='' E=1 N='' RN='' R=''
  head 'Установить загрузчик GRUB, указать диски' 1
  mdstat; findisks
  echo -e "$LS Устройства в системе:\n$LN$LS $DISKSH"
  for ((I=0; I<${#DISKS[*]}; I++)); do echo -e "$LS ${DISKS[$I]}"; done
  echo -e "$LN$LS Укажите диски:   (участники работающих массивов пропускаются, разметка клонируется после первого размеченного)\n$LE"
  I=1
  while [[ $E == 1 ]]; do
    echo "$LS$LS$LS $DISKSH"
    MO=('Завершить (Установить GRUB)')
    while read L; do
      if [[ $(ex "$L") == 'disk' ]]; then N=$(ex "$L" 1)
        if ! [[ " $RN " =~ " $N " ]]; then opt "$L"; fi
      fi
    done <<< "$(echo -e "$D")"
    menu 1
    while :; do R=$(waitkey "Введите $I диск: ")
      if [[ $R == 0 ]]; then exit 0
      elif [[ $R == 1 ]]; then E=0; break
      else R=$(optget "$R")
        if [[ -n $R ]]; then ((I++)); R=$(ex "$R" 1); RN+="$R "; echo -e "$LN$LS Выбраны: $RN \n$LE"; break; fi
      fi
    done
  done
  if [[ -n $RN ]]; then L=($RN)
    for ((I=0; I<${#L[*]}; I++)); do
      echo -e "$LN$LS Устанавливаю загрузчик GRUB на /dev/${L[$I]}"
      sudo grub-install "/dev/${L[$I]}"
    done
    echo $LE
    sudo update-grub
  fi
}
grub1(){
  local A='' I=0 D=''
  if [[ -z $1 ]]; then
    head 'Установить загрузчик GRUB на все диски в RAID массиве' 1
    raidlist grub1
  else
    A=($(ex "$2" 0 '  (' 0))
    for ((I=4; I<${#A[*]}; I++)); do
      D=$(ex "${A[$I]}" 0 '[')
      D=$(rtrim "$D" '[0-9]')
      echo -e "$LN$LS Устанавливаю загрузчик GRUB на /dev/$D"
      sudo grub-install "/dev/$D"
    done
    echo $LE
    sudo update-grub
  fi
}
efind(){
  local L='' A='' T='' I=0 D=$(lsblk -I 8 "/dev/$1" -o NAME,TYPE)
  while read L; do
    if [[ $L =~ 'part' ]]; then A=$(ex "$L"); A=$(ex "$A" 1 '─'); I=$(endint "$A")
      T=$(sudo sgdisk -p "/dev/$1" -i "$I" | grep 'Partition name:')
      if [[ $T =~ 'EFI System Partition' ]]; then
        T=$(df "/dev/$1$I" | grep "/dev/$1$I")
        echo $(ex "$T" 2)
        break
      fi
    fi
  done <<< "$(echo -e "$D")"
}
grubefi1(){
  local A='' I=0 D='' S='' X=0 R=''
  if [[ -z $1 ]]; then
    head 'Скопировать EFI раздел с источника в RAID на остальные диски в RAID массиве и установить GRUB если найдёт BIOS Boot Partition(bios_grub)' 1
    raidlist grubefi1
  else
    A=($(ex "$2" 0 '  (' 0))
    for ((I=4; I<${#A[*]}; I++)); do
      D=$(ex "${A[$I]}" 0 '[')
      D=$(rtrim "$D" '[0-9]'); A[$I]=$D
      X=$(efind "$D")
      [[ -z $X ]] && X=0
      ((X > M)) && S=$D
    done
    X=0
    if [[ -n $S ]]; then
      MO=("Возможно определён источник копирования EFI: /dev/$S выбрать его"
      'Указать диск источник (с которого произойдёт копирование EFI раздела)')
      menu 2
      while :; do R=$(waitkey)
        if [[ $R == 0 ]]; then exit 0
        elif [[ $R == 1 ]]; then STAGE=1; return 0
        elif [[ $R == 2 ]]; then break
        elif [[ $R == 3 ]]; then X=1; break; fi
      done
    fi
    if [[ $X == 1 ]]; then MO=()
      for ((I=4; I<${#A[*]}; I++)); do D=${A[$I]}; opt "/dev/$D"; done
      menu 2
      while :; do R=$(waitkey 'Выберете диск источник: ')
        if [[ $R == 0 ]]; then exit 0
        elif [[ $R == 1 ]]; then STAGE=1; return 0
        else D=$(optget "$R")
          if [[ -n $D ]]; then S=$(ex "$D"); S=$(ex "$S" 1 '/dev/'); break; fi
        fi
      done
    fi
    X=0
    for ((I=4; I<${#A[*]}; I++)); do D=${A[$I]}
      if [[ $S != $D ]]; then
        echo -e "$LN$LS Устанавливаю загрузчик на /dev/$D"
        R=$(sgdck "$S" "$D") # Установить grub/скопировать EFI
        [[ $R == 1 ]] && X=1
      fi
    done
    if [[ $X == 1 ]]; then echo $LE; sudo update-grub; fi
  fi
}
grubefi(){ # Пакет grub-pc глючит при sudo dpkg-reconfigure grub-pc не выводит список дисков под U/Kubuntu
  # sudo dpkg-reconfigure grub-pc или sudo grub-install /dev/sda (is equivalent to grub-install --boot-directory=/boot/ /dev/sda) && sudo update-grub
  local R=''
  head 'Установить загрузчик GRUB' 1
  MO=('Скопировать EFI раздел с источника в RAID на остальные диски в RAID массиве и установить GRUB если найдёт BIOS Boot Partition(bios_grub)\n'
  'Установить загрузчик GRUB на все диски в RAID массиве'
  'Установить загрузчик GRUB, указать диски')
  menu 2
  while :; do R=$(waitkey)
    if [[ $R == 0 ]]; then exit 0
    elif [[ $R == 1 ]]; then STAGE=1; return 0
    elif [[ $R == 2 ]]; then grubefi1; break
    elif [[ $R == 3 ]]; then grub1; break
    elif [[ $R == 4 ]]; then grub2; break; fi
  done
  anykey 1
}
info(){
  local F=0 A='' L='' N='' R='' U='' M='/etc/mdadm/mdadm.conf'
  if [[ -e $M ]]; then
    A=$(cat $M)
    while read L; do
      if [[ $L =~ 'ARRAY' ]]; then
        if ((F == 0)); then echo -e "$LS$BG Предопределены массивы в $M: $BN"; F=1; fi
        echo "$LS $L"
      fi
    done <<< "$(echo -e "$A")"
    ((F == 1)) && echo "$LE"
  fi
  F=0; A=$(cat /etc/fstab); mdstat
  while read L; do
    R=$(ex "$L" 0 ' : ')
    U=$(uuid "$R")
    [[ -z $U ]] && U='vee4hu3pui1eshecheiTeer8amieh0UNai6fier'
    while read N; do
      if [[ $N =~ $U || $N =~ "/dev/$R" ]]; then
        if ! [[ $N =~ '#' ]]; then
          if [[ $F == 0 ]]; then echo -e "$LS$BG Смонтированные массивы в /etc/fstab: $BN"; F=1; fi
          echo "$LS $R : $N"
        fi
      fi
    done <<< "$(echo -e "$A")"
  done <<< "$(echo -e "$MDS")"
  ((F == 1)) && echo "$LE"
  F=0
  while read L; do
    if [[ -n $L ]]; then
      if [[ $F == 0 ]]; then echo -e "$LS$BG Работающие массивы в /proc/mdstat: $BN"; F=1; fi
      if [[ $L =~ '_' ]]; then
        if [[ $L =~ '(F' ]]; then err 'Диск дал сбой, помечен (F):' 'Внимание!'
        else err 'Неполный массив:' 'Внимание!'; fi
      fi; echo "$LS $L"
    fi
  done <<< "$(echo -e "$MDS")"
  ((F == 1)) && echo "$LE"
}
more(){
  local A='' R='' C='' P='' M='' H=''
  head 'Операции с дисками и операционной системой' 1
  if ! [[ -e /sbin/gdisk ]] || [[ ! -e /bin/lsblk && ! -e /usr/bin/lsblk ]]; then err 'Не установлено программное обеспечение для обеспечения функций скрипта md. Пункт 7).' 'Внимание!'; fi
  if ! [[ -e /sbin/mdadm ]]; then
    err 'Не установлено программное обеспечение mdadm для RAID. Пункт 8).' 'Внимание!'
    echo -e "$LS Скрипт написан и проверен под RAID 1. Перед использованием других схем - проверьте все функции!\n$LE"
  fi
  echo -e "$LS Комментарий к пункту 10: При добавлении в автозагрузку, будет проверка (во время запуска ОС) состояния RAID массивов.\n$LS В случае проблем, на рабочем столе появится предупреждающий значок. (Для сервера по crontab и отсылка в скрипт.)\n$LS Скрипт скопирует себя в /usr/bin/md, можно будет вызывать из консоли как md. Добавит ярлык запуска в меню Настройка.\n$LE"
  MO=('Быстрый тест здоровья дисков\n' 'Стереть диск' 'Разметить диск/и' 'Отформатировать раздел диска\n'
  'Создать точку(директорию) монтирования и примонтировать раздел диска'
  'Удалить точку монтирования\n' 'Создать/Удалить своп файл /swapfile\n'
  'Установить ПО для обеспечения работоспособности скрипта: bc curl gdisk lsblk(util-linux) smartctl(smartmontools)'
  'Установить ПО из 9п. и для RAID: mdadm; скопировать скрипт в /usr/bin/md и добавить в автозагрузку\n'
  'Настроить оповещение на почту и пр. (dpkg-reconfigure mdadm, отдельно настройте почтовый сервер)')
  menu 2
  while :; do R=$(waitkey)
    if [[ $R == 0 ]]; then exit 0
    elif [[ $R == 1 ]]; then STAGE=1; return 0
    elif [[ $R == 2 ]]; then diskshealth; anykey; break
    elif [[ $R == 3 ]]; then disknil; break
    elif [[ $R == 4 ]]; then diskparted; break
    elif [[ $R == 5 ]]; then partformat; break
    elif [[ $R == 6 ]]; then partmount; break
    elif [[ $R == 7 ]]; then delmount; break
    elif [[ $R == 8 ]]; then swap; break
    elif [[ $R == 9 ]]; then
      # smartctl; lsblk - util-linux (ранее util-linux-ng)    grub-pc grub-efi
      # Комментарий к пунктам 8), 9) grub-pc выведет установку загрузчика - не отказывайтесь, укажите диск и установите!\n
      sudo apt install -y bc
      sudo apt install -y curl
      sudo apt install -y gdisk
      sudo apt install -y util-linux
      sudo apt install -y smartmontools
      head 'Установлены программы для управления дисковой системой и обеспечения работоспособности скрипта md.'
      anykey 1; break
    elif [[ $R == 10 ]]; then
      if ! [[ -e /usr/bin/md ]]; then
        echo $LE
        sudo cp -v $(readlink -f "$0") /usr/bin/ # разворачивает путь ./md -> /tmp/md
        sudo chmod +x /usr/bin/md
      fi
      if [[ $USER == 'root' ]]; then
        echo -e "$LN$LS Формат [0-23]: * = каждый час; */5 = каждые 5 часов; 3 = в 3 часа; 7-19 = каждый час с 7 до 19 вечера; 5,7 = в 5 и 7 часов утра"
        while :; do R=$(waitkey 'Введите час: ')
          R=${R//[^0-9\*\/,-]}
          if [[ -n $R ]]; then H=$R; break; fi
        done
        echo -e "$LN$LS Формат [0-59]: * = каждую минуту; */5 = каждые 5 минут; 3 = в 3 минуты; 7-9 = с 7 до 9 минуты; 3,5 = в 3 и 5 минут"
        while :; do R=$(waitkey 'Введите минуту: ')
          R=${R//[^0-9\*\/,-]}
          if [[ -n $R ]]; then M=$R; break; fi
        done
        if [[ -n $H && -n $M ]]; then # минута час день месяц день_недели /путь/к/исполняемому/файлу
          A=''; rm -f /tmp/fsencrontab.txt
          P=$(crontab -l)
          while read C; do
            if [[ -n $C && ! $C =~ '/usr/bin/md test' ]]; then A+="$C\n"; fi
          done <<< "$(echo -e "$P")"
          echo -e "$A$M $H * * * /usr/bin/md test 2>&1\n" >> /tmp/fsencrontab.txt
          crontab /tmp/fsencrontab.txt
        fi
      else
        mkdir -p ~/.config/autostart/
        echo -e '[Desktop Entry]\nType=Application\nName=RAID test\nComment=Проверка RAID массивов\nExec=/usr/bin/md test\nIcon=drive-harddisk' > ~/.config/autostart/raid-test.desktop
        chmod +x ~/.config/autostart/raid-test.desktop
        echo -e '[Desktop Entry]\nType=Application\nName=RAID control\nComment=Управление RAID массивами\nTerminal=true\nExec=/usr/bin/md\nCategories=Settings;\nIcon=drive-harddisk' | sudo tee /usr/share/applications/raid-control.desktop > /dev/null # /sbin/sgdisk in package gdisk # cgdisk
      fi
      sudo apt install -y bc
      sudo apt install -y curl
      sudo apt install -y gdisk
      sudo apt install -y util-linux
      sudo apt install -y smartmontools
      sudo apt install -y mdadm
      syncspeedset 6 # Лимит скорости синхронизации 3.2 ГБ/с
      head 'Установлены программы для управления дисковой системой и RAID массивами. Перезагрузите компьютер, mdadm корректно пересоберёт массивы..'
      MO=('Перезагрузить' 'Продолжить без перезагрузки')
      menu 1
      while :; do R=$(waitkey)
        if [[ $R == 0 ]]; then exit 0
        elif [[ $R == 1 ]]; then sudo reboot
        elif [[ $R == 2 ]]; then STAGE=1; break; fi
      done
      break
    elif [[ $R == 11 ]]; then sudo dpkg-reconfigure mdadm; anykey 10; break; fi
  done
}
raidmenu(){
  local R=''
  head 'Операции с RAID массивами' 1
  MO=('Проверить RAID массив на ошибки e2fsck -fpv'
  'Тест скорости записи и чтения RAID массива\n'
  'Остановить RAID'
  'Запустить RAID'
  'Переименовать RAID массив (не видит незапущенные массивы)\n'
  "Удалить RAID массив (можно удалять некорректно собранные массивы без стирания дисков и восстанавливать\n $LS $LS $LS $LS $LS $LS $LS $LS $LS (см. пункт 3 главного меню - Собрать ранее существовавший RAID))\n"
  'Создать RAID массив (шаг 1)'
  'Создать RAID массив (шаг 2) отформатировать после синхронизации\n'
  'Расширение RAID массива (при замене дисков на большие или добавлении дисков)\n'
  'Изменить скорость синхронизации дисков (по умолчанию NVMe = 3.2 ГБ/с)'
  'Запустить синхронизацию RAID массива')
  menu 2
  while :; do R=$(waitkey)
    if [[ $R == 0 ]]; then exit 0
    elif [[ $R == 1 ]]; then STAGE=1; break
    elif [[ $R == 2 ]]; then raidcheck; break
    elif [[ $R == 3 ]]; then testspeed; break
    elif [[ $R == 4 ]]; then raidstop; break
    elif [[ $R == 5 ]]; then raidstart; break
    elif [[ $R == 6 ]]; then raidrename; break
    elif [[ $R == 7 ]]; then raidremove; break
    elif [[ $R == 8 ]]; then raidadd; break
    elif [[ $R == 9 ]]; then raidformat; break
    elif [[ $R == 10 ]]; then raidgrow; break
    elif [[ $R == 11 ]]; then syncspeed; break
    elif [[ $R == 12 ]]; then raidsync; break; fi
  done
}
mainmenu(){
  local F=0 S=0 A='' R='' L=''
  while :; do # Проверка идёт ли синхронизация..
    clear; F=0; A=$(cat /proc/mdstat)
    while read L; do
      if [[ $L =~ 'resync =' || $L =~ 'recovery =' ]]; then F=1; break; fi
    done <<< "$(echo -e "$A")"
    if ((F == 1)); then
      clear; echo -e "$LN$LS Во время синхронизации запрещено совершать действия с RAID массивом!\n$LS Если произошёл сбой/поломка, дождитесь восстановления массива, затем помечайте сбойным и вытаскивайте/заменяйте диск..\n$LN\n$A\n\n$L"
      if [[ $S == 0 ]]; then
        MO=('Наблюдать за синхронизацией'
        'Изменить скорость синхронизации дисков (по умолчанию NVMe = 3.2 ГБ/с)'
        'Показать диски в системе'
        'Детальная информация о RAID массиве'
        'Установить загрузчик GRUB или скопировать EFI (если RAID на дисках с операционной системой)'
        'Удалить RAID массив')
        menu 1;
        while :; do R=$(waitkey)
          if [[ $R == 0 ]]; then exit 0
          elif [[ $R == 1 ]]; then S=1; break
          elif [[ $R == 2 ]]; then syncspeed; break
          elif [[ $R == 3 ]]; then disksinfo; break
          elif [[ $R == 4 ]]; then raidinfo; break
          elif [[ $R == 5 ]]; then grubefi; break
          elif [[ $R == 6 ]]; then raidremove; break; fi
        done
      else sleep 5; fi
      continue
    fi
    break
  done
  head 'Управление RAID массивами (mdadm)' 1
  info
  MO=('Показать диски в системе'
  'Детальная информация о RAID массиве\n'
  'Собрать ранее существовавший RAID (шаг 1 после переустановки Linux (если не подцепился на автомате))'
  'Создать точку(директорию) монтирования и примонтировать RAID. (шаг 2..)\n'
  'Удалить диск из RAID массива (шаг 1 при поломке диска)'
  'Добавить диск (раздел) в RAID массив (шаг 2 при поломке диска)\n'
  'Установить загрузчик GRUB или скопировать EFI (если RAID на дисках с операционной системой)\n'
  'Меню операций с RAID массивами'
  'Меню операций с дисками и операционной системой')
  menu 1; R=$(waitkey); STAGE=$(int "$R");
  ((STAGE != 0)) && ((STAGE++))
}
if [[ ! -e /sbin/gdisk || ! -e /sbin/mdadm ]] || [[ ! -e /bin/lsblk && ! -e /usr/bin/lsblk ]]; then STAGE=10; fi
while ((STAGE != 99)); do
  if ((STAGE == 0)); then break # STAGE=99
  elif ((STAGE == 1)); then mainmenu
  elif ((STAGE == 2)); then disksinfo
  elif ((STAGE == 3)); then raidinfo
  elif ((STAGE == 4)); then raidassemble
  elif ((STAGE == 5)); then raidmount
  elif ((STAGE == 6)); then diskremove
  elif ((STAGE == 7)); then diskadd
  elif ((STAGE == 8)); then grubefi
  elif ((STAGE == 9)); then raidmenu
  elif ((STAGE == 10)); then more
  fi
done
