A muito tempo que não escrevo um script em bash e percebo que estou muito enferrujado. No entanto, depois de algumas tentativas e erros, com muitas consultas ao man e a rede, o script saiu.

A ideia deste script é eliminar um potencial acidente, a qual já me deixou sem sistema operacional e sem meus preciosos documentos, arquivos, planilhas, projetos, …, ao menos umas três vezes ao longo destes últimos 15 anos de computação. A estória somente não foi pior nas últimas ocorrências pois me tornei sistemático o suficiente para manter backups, ao menos bimestrais, nos últimos anos.

O problema vem na execução de comandos básicos como o dd e o fdisk em meros pendrives de uso geral. Procedimentos comuns que devemos fazer sempre que instalamos um sistema novo em alguma máquina, através do pendrive, ou simplesmente se deseja formatar o dispositivo. Não foi uma vez em que troquei o /dev/sdb por um /dev/sda, e neste momento o sistema foi para o saco.

Na última trágica ocasião em que isto aconteceu, me lembro que estava do meio para o final do semestre na universidade, e perdi notas de provas, de trabalhos e outras atividades, enquanto que o meu backup mais recente datava de quase um ano atrás.

Na ocasião fui obrigado a aprender a utilizar o TestDisk para recuperar as informações perdidas do disco rígido recém particionado/formatado e felizmente recuperei a maior parte das informações primordiais. Na ocasião, até pensei em fazer um tutorial a respeito do processo, no entanto o desespero para a recuperação dos dados foi tamanho que achei mais conveniente concentrar minha minha atenção em recuperar os dados, deixando o texto para uma outra ocasião. O TestDisk está anotado em minha lista de futuros artigos.

A intenção deste texto não é ser um texto de orientação a criação de bash scripts, mas apenas documentar uma experiencia em tentar tornar o uso do sistema menos destrutivo, em tarefas básicas.

A Ideia do Script

A ideia do script é que este seja inteligente o suficiente para impedir que erros tolos, como formatar um disco em uso não ocorram. Para isto tracei as seguintes premissas para o script:

  • Não particionar/formatar dispositivos com partições montadas;
  • Não particionar/formatar discos definidos como disco de sistema.

A primeira premissa é feita na linha 18, onde os dispositivos montados são listados na variável MOUNTEDDEVS, através de uma consulta ao comando df.

Quanto à segunda premissa, havia a possibilidade de consultar o arquivo /etc/fstab. Mas devido a possibilidade do uso dos códigos UUID para indicar as partições dos disco, o que virou padrão na maioria das distribuições, optei por adicionar os disco do sistema em uma variável, SYSDEVICES, como dispositivos reservados para o sistema. Esta variável esta definida na linha 20 com os dispositivos sda e sdb, que são os dois discos que compõem o meu sistema.

Como medida de segurança adicional, o aplicativo apenas formata os discos do sistema se estes estiverem desmontados e a opção --force for passada para o script.

Como particionamento/formatação de um dispositivo é algo muito destrutivo, o script ainda irá pedir uma confirmação antes de executar os comandos, linha 88.

O dispositivo a ser particionado/formatado pode ser passado como parâmetro com a opção -d <device>. Se este não for passado, o último dispositivo conectado a uma das portas USB será selecionado com uma consulta nas últimas linhas do comando dmesg, linha 48.

Adicionei ainda: uma opção de warning, -w, para simular os comandos sem de executá-los; uma opção para adicionar um label para o dispositivo, -n; além do help do script, -h.

O script completo segue abaixo:

#!/bin/bash 
#
# Create a new FAT32 partition on the stick and write a FAT32 
# filesystem on it.
#

function help()
{
    echo "Use: # `basename $0` [options]:"
    echo "    Where:"
    echo "        -d "
    echo "        -n "
    echo "        --force (to force format of SYSDEVICES)"
    echo "        -w (warning mode. Don't format)"
    echo "        -h for help"
}

MOUNTEDDEVS=`df -h | grep '^\/dev\/sd[a-z]' | cut -c-8 | sort | uniq | cut -c6-`
FORCE=false
SYSDEVICES="sda sdb"
WARN=false

while [ ! -z $1 ]; do
    case $1 in
        '-d')
            DEV=$2
            shift
            ;;
        '-n')
            DEVNAME=$2
            shift
            ;;
        '--force')
            FORCE=true
            ;;
        '-w')
            WARN=true
            ;;
        '-h'|*)
            help
            exit 0
            ;;
    esac
    shift
done

if [ -z "$DEV" ]; then
    DEV=`dmesg | tail | grep '\[.*\] *sd[bcdefg]' | rev | cut -d" " -f1 | rev | tr -d ':0123456789' | head -n1`
fi

if [ -z "$DEVNAME" ]; then
    DEVNAME="USBDisk"
fi

echo -n "Check by mounted devices: "
for dev in $MOUNTEDDEVS; do
    if [ "$dev" == "$DEV" ]; then
        # Abort format of mounted devices
        echo "mounted..."
        echo "Device /dev/$DEV is in use. To format it you need umount all partitions this device before."
        df -h | grep "^\/dev\/$DEV"
        exit 0
    fi
done
echo "Device isn't mounted..."

echo -n "Check by System devices: "
SYSDEV=false
for dev in $SYSDEVICES; do
    if [ "$dev" == "$DEV" ]; then
        SYSDEV=true
        if $FORCE; then
            echo "Is a system device, but forced enable..."
            break
        fi
        echo -e "Is a system device...\n"
        echo "Use --force option to format a system device, or edit SYSDEVICES variable in this script."
        exit 0
    fi
done
if ! $SYSDEV; then
    echo "Is not a system device..."
fi

echo -ne "\nFormat device /dev/${DEV} with name ${DEVNAME} (y/N)? "
read ANS

if [ "$ANS" != "Y" -a "$ANS" != "y" ]; then
    exit 0
fi

echo -e "\nClear first 10MB..."
if $WARN; then
    echo "dd if=/dev/zero of=/dev/$DEV bs=1M count=10"
else
    dd if=/dev/zero of=/dev/$DEV bs=1M count=10
fi

echo -e "\nRemove partitions in device /dev/${DEV} ..."

if $WARN; then
    echo "fdisk /dev/$DEV <

Uso do Script

Chamei o script de usbfat32, uma vez que no momento é um script especializado para formatar dispositivos USB em FAT32. Mas é possível que adiante estenda a ideia. As linhas a seguir mostram alguns exemplos de uso:

Limpa a tabela de partição do dispositivo /dev/sdc, formata em FAT32 e configura o nome do dispositivo para 'Alves':

usbfat32 -d sdc -n Alves

Limpa a tabela de partição do dispositivo recém inserido a porta USB, formata em FAT32 e configura o nome do dispositivo para 'Alves':

usbfat32 -n Alves

O mesmo que o comando anterior, mas sem executar nada:

usbfat32 -n Alves -w

A próxima linha de comando mostra o que acontece ao tentar formatar um dispositivo montado:

usbfat32 -d sda
Check by mounted devices: mounted...
Device /dev/sda is in use. To format it you need umount all partitions this device before.
/dev/sda1        25G  8.4G   17G  34% /
/dev/sda4       102G   14G   89G  14% /home

Neste último comando o dispositivo está desmontado, mas está assinalado como sendo dispositivo do sistema:

usbfat32 -d sdb
Check by mounted devices: Device isn't mounted...
Check by System devices: Is a system device...

Use --force option to format a system device, or edit SYSDEVICES variable in this script.

Neste último caso, se usar a opção --force o dispositivo /dev/sdb será particionado e em seguida formatado em FAT32.

Conclusões

O script está com o código ainda tosco, necessitando de um pouco de lapidação, como disse estou muito sem prática. No entanto faz bem o seu papel. Acho que assim ao menos fica reduzida a probabilidade de formatar o HD no lugar do pendrive. Os interessados em melhorar a ideia estão convidados a compartilhar.