Глючный скрипт и его исправление

Нижеследующий вариант неудачный и кривой, благодарю за подсказку хорошего анонима.
Вот нормальный вариант.
Копия на LJR

Преамбула

Понадобилось определить файловую систему не смонтированного раздела из ОС. Причем желательно так, чтобы способ работал в нескольких разных дистрибутивах.
Кратко пройдусь по способам, описанным в [1]

1. Команда df: не подошла, не смонтированные разделы не видит

2. Использование команды mount и просмотр файла /etc/fstab, та же беда, что логично.
3. Использование команды fsck. Способ довольно сомнительный, и команда может не везде быть.
4. Наиболее годным оказался способ с использованием команды file. Во всяком случае, для линуксовых ФС она почти всегда выдавала то, что нужно, а когда выдавала не совсем то, это можно было немного подправить вручную, но об этом далее. А вот с виндовыми разделами выходила ерунда, и анализировать вывод было неудобно, и почему-то путались разделы FAT/FAT32.

Для экспериментов сделал такую структуру диска, с несколькими ФС, как Linux, так и DOS/Windows:

Далее кратко опишу скрипт detectfs, запускаемый с единственным параметром — нужное устройство, например, detectfs /dev/sda1

Тип раздела и fdisk

В Линукс есть консольный менеджер разделов диска fdisk, если запустить его с опцией -l, то он выведет список всех разделов для всех дисков компьютера.

В выводе fdisk -l для моей цели интересна колонка Id, в которой указывается тип раздела, который не следует путать с типом файловой системы на разделе. Но все-таки они коррелируют, на разделе типа 0x7 (NTFS/HPFS) будет файловая система NTFS (или файловая система HPFS, используемая OS/2, впрочем она сейчас встречается очень редко), на разделе 0xf — FAT32, а на разделе 0x6 — FAT16. На разделе типа 0x83 может присутствовать любая файловая система Linux (EXT2, EXT3, EXT4, BTRFS). Полный список идентификаторов типов разделов можно посмотреть в источнике [2]

Полную справку по fdisk можно посмотреть с помощью man fdisk или в [3]

Получение нужного Id из вывода fdisk

Для этой цели можно применить grep и awk.
Минус fdisk в том, что в зависимости от версии, колонка Id, может со своего места «уплыть»:

Разделители между колонками — пробелы, потому задать нужный номер для awk «с конца» тоже не получится, идущие подряд пробелы awk воспримет, как один, так что в этом проблемы не будет. Но номер колонки придется вычислять. Как это делать, я описывал буквально в прошлом посту [4], так что не буду подробно останавливаться на этом, сразу приведу команду, записывающую номер колонки в переменную FLD.

FSTYPE="" #заодно заведем переменную для хранения типа ФС
FLD=`fdisk -l|grep -w "Id"|head -n1|sed 's/ /\n/g'|nl|grep -w "Id"|awk '{print $1}'`

Далее необходимо обратить внимание на колонку Boot — если раздел активный, там будет стоять *, а если нет, то она окажется пустой, и awk, когда будет извлекать данные о разделе, ее проигнорирует. Так что номер колонки надо уменьшить на 1:

let "FLD=FLD-1"

Внимание! Если дисков несколько, для каждого будет выведена своя таблица, соответственно строк со словом Id будет несколько. Чтобы на этапе вычисления не произошло ошибки, надо оставить только первую. Это делает команда head -n1.
А с символом * разберемся, когда будем получать информацию о нужном разделе:

1. Выведем информацию обо всех разделах всех дисков:

fdisk -l

2. Отфильтруем grep‘ом только строку с устройством, заданном в первом параметре скрипта ($1):

grep -w "$1"

3. Избавляемся от символа *, заменяя его на пробел:

sed 's/*/ /g'

4. Передаем awk переменную с номером колонки из bash-скрипта, и вытаскиваем нужный Id:

awk -v fld="${FLD}" '{print $fld}'`

5. Команда целиком:

VOLID=`fdisk -l|grep -w $1|sed 's/*/ /g'|awk -v fld="${FLD}" '{print $fld}'`

Не линуксовые и расширенные (extended) разделы

Понятно, что скрипт на полную универсальность не претендует, так что анализа всех возможных id тут не будет, но наиболее часто встречающиеся определяем с помощью case[5]:

#Сначала проверим, есть ли устройство вообще

if [ -z $VOLID ];then
    echo "$1 NO DEVICE"
    exit 2
fi

case $VOLID in
    6)
	FSTYPE="FAT16"
    ;;    
    7)
	FSTYPE="NTFS"
    ;;
    b)
	FSTYPE="FAT32"
    ;;
    f)
	FSTYPE="EXTENDED"
    ;;
    82)
	FSTYPE="SWAP"
    ;;
    83)
	FSTYPE="LINUX"
    ;;
    85)
	FSTYPE="LINUX EXTENDED"
    ;;
    *)
	FSTYPE="OTHER"
    ;;
esac

Если тип раздела — LINUX, то надо попытаться определить линуксовую FS:

if [[ "$FSTYPE" == "LINUX" ]]; then
    linux_fs $1
fi

Функция скрипта linux_fs попытается определить конкретную FS на Linux-разделе и сохранит результат в переменную FSTYPE.

Выводим результат:

echo "$1 $FSTYPE"

Файловые системы Linux

Определял только те, которые мне были нужны: EXT2, EXT3, EXT4 и BTRFS.
Вызов команды file:

file -sL <filename>
где
-L — указание программе, что если ей указана символическая ссылка, то надо работать с файлом, на который указывает ссылка.
-s — указание, что файл является специальным файлом (в нашем случае — файлом блочного устройства [6])
<filename> — имя файла.

Но оказалось, что и в использовании file есть подводный камень, в одном из дистрибутивов она не смогла определить BTRFS, и определила EXT4 как EXT3 с пометкой (large files).

Все нормально:

Баг с BTRFS и EXT4:

EXT2, EXT4 и BTRFS сделал следующим образом:

1. Функция записывает в переменную результат работы команды file:

LINUXDATA=`file -sL $1`

2. Выводит результат на консоль, передает его grep, grep подсчитывает количество найденных строк с помощью ключа , результат записывается в переменную.

CTR=`echo "$LINUXDATA"|grep -w -c "ext2"`

3. Далее проверяется количество строк. Если их 0, значит проверяется другая FS аналогичным образом, если строка есть — нашли нужную FS. Переменная FSTYPE соответственно обновляется, и происходит возврат из функции в основной скрипт (return):

CTR=`echo "$LINUXDATA"|grep -w -c "ext2"`
    if [ $CTR -ne 0 ]; then
	FSTYPE="EXT2"
	return
    fi

С неверным определением BTRFS решил не бороться, а для EXT3/EXT4 сделал дополнительную проверку. Если файловая система EXT3 (EXT4 проверяется раньше), делаю дополнительный grep, и проверку наличие фразы «large files«:

CTR=`echo "$LINUXDATA"|grep -w -c "ext3"`
    if [ $CTR -ne 0 ]; then
	CTR=`echo "$LINUXDATA"|grep -c "large files"` #for EXT4 not full support
	if [ $CTR -ne 0 ]; then
	    FSTYPE="EXT4"
	else
	    FSTYPE="EXT3"
	fi
	return
    fi

Если до конца функции нигде не вышли return‘ом, указываем, что файловая система Linux, но какая не определена:

FSTYPE="UNKNOW LINUX FS"

Код функции на PasteBin

Минусы

— На мой взгляд, функция определения линуксовых FS кажется достаточно топорной.
— Скрипт криво работает с разделами, которые добавляет kpartx, например, разделы виртуального жесткого диска, висящего на loop-устройстве, но мне это пока не надо.

Источники

1. Как определить тип файловой системы в Linux? Копия
2. List of partition identifiers for PCs Копия
3. fdisk
4. Определение номера колонки таблицы по содержимому (заголовку), для вывода ее awk Копия
5. Краткая справка по оператору case
6. Блочное устройство

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *