Я хочу создать текстовый файл с расширением ".bulka". Мне это нужно для того, чтобы при клике по этому текстовому файлу, он открывался с помощью определённой программы. Чтобы обычный текстовый файл открывался с помощью одной программы, например, Leafpad, а текстовый файл с расширением ".bulka" - с помощью VSCodium.
Создадим XML-файл "~/.local/share/mime/packages/application-x-bulochka.xml":
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-bulka">
<glob pattern="*.bulka"/>
</mime-type>
</mime-info>
Обновим базу данных MIME:
update-mime-database ~/.local/share/mime
Проверим, видит ли система наш новый тип:
grep -R "application/x-bulka" ~/.local/share/mime/
Вот вывод, который я получил:
/home/mark/.local/share/mime/globs:application/x-bulka:*.bulka
/home/mark/.local/share/mime/types:application/x-bulka
/home/mark/.local/share/mime/application/x-bulka.xml:
grep: /home/mark/.local/share/mime/mime.cache: двоичный файл совпадает
/home/mark/.local/share/mime/packages/application-x-bulochka.xml:
/home/mark/.local/share/mime/globs2:50:application/x-bulka:*.bulka
Теперь я хочу создать свой файл с расширением ".bulka":
echo "Первая строка, которую мы вставляем в файл." >> myfile.bulka
echo "Вторая строка" >> myfile.bulka
Этот файл (myfile.bulka) по факту является типом "text/plain". Но мы определили в файле application-x-bulochka.xml, что все файлы с расширением ".bulka" должны определяться как наш новый тип "application/x-bulka". Давайте попробуем определить тип файла разными способами:
# с помощью утилиты file:
file --mime-type myfile.bulka
# myfile.bulka: text/plain
# с помощью xdg-mime
xdg-mime query filetype myfile.bulka
# text/plain
# с помощью gio:
gio info myfile.bulka | grep content-type
# standard::content-type: application/x-bulka
# standard::fast-content-type: application/x-bulka
Как видим, разные утилиты определяют тип по-разному. Утилита file говорит, что наш файл имеет тип "text/plain". А вот gio говорит, что тип нашего файла: "application/x-bulka". А вот скрипт xdg-mime под капотом использует утилиту file, поэтому результат такой же, как когда мы спрашиваем тип у file.
Всё дело в том, что gio, когда пытается определить тип файла, опирается на нашу локальную базу данных MIME-типов, в которую мы добавили тип "application/x-bulka". А вот file залезает в файл и читает его содержимое и на основе содержимого определяет тип файла.
Файловые менеджеры, такие как PCManFM или Thunar, определяют тип файла на основе нашей MIME-базы данных. Поэтому когда мы кликаем по файлу, чтобы он открылся, система по расширению .bulka понимает, что это наш тип "application/x-bulka". Но если мы хотим, чтобы наш файл открывался с помощью нужной нам программы, то нам надо ещё привязать наш MIME-тип к конкретной программе.
# Проверим, какое приложение является дефолтным для нашего типа:
xdg-mime query default application/x-bulka
# скорее всего эта команда ничего не вернёт, потому что мы пока не указывали ...
# ... дефолтное приложение для нашего MIME-типа.
Теперь я хочу сделать так, чтобы при клике по файлу с расширением .bulka, он открывался с помощью программы VSCodium:
# Привязываю VSCodium к типу application/x-bulka:
xdg-mime default codium-wayland.desktop application/x-bulka
# Ещё раз проверяю, какое приложение привязано к нашему типу:
xdg-mime query default application/x-bulka
# codium-wayland.desktop
# Проверю также, какое приложение у меня привязано к обычному текстовому файлу:
xdg-mime query default text/plain
# leafpad.desktop
Теперь я могу в файловом менеджере кликать по текстовому файлу myfile.bulka и он будет открываться с помощью программы VSCodium. При этом обычный текстовый файл myfile.txt откроется с помощью Leafpad.
НО! Важно, что все программы опредляют тип по-своему. Если большинство файловых менеджеров опираются на базу данных MIME-типов в нашей системе, то некоторые программы используют утилиту file для определения типа файла. Например, если мы попробуем открыть файл с помощью xdg-open, то этот скрипт определит тип файла с помощью утилиты file и решит, что у нас обычный текстовый файл "text/plain" и соответственно откроет файл с помощью программы Leafpad.
xdg-open myfile.bulka
# должен открыться с помощью Leafpad
Файловый менеджер lf также использует file, поэтому откроет наш файл через Leafpad.
Когда я привязываю программу к MIME-типу с помощью xdg-mime, привязка записывается в файл mimeapps.list.
# ~/.config/mimeapps.list:
# ~/.local/share/applications/mimeapps.list:
# /usr/share/applications/mimeapps.list:
# /usr/local/share/applications/mimeapps.list:
[Default Applications]
application/x-bulka=leafpad.desktop
Ещё несколько файлов, раскиданных по системе:
# /etc/mime.types
# /usr/share/mime/mime.cache
# ~/.local/share/mime/mime.cache
# /usr/share/applications/mimeinfo.cache
# ~/.local/share/applications/mimeinfo.cache
# ~/.local/share/applications/mimeapps.list
# ~/.config/mimeapps.list
Если я не привязал тип text/x-bulka к какой-либо программе и кликаю по файлу myfile.bulka, то PCManFM предложит мне самому выбрать программу, которой я хочу открывать этот файл. Я выбираю VSCodium и файл открывается с помощью VSCodium. После этого привязка text/x-bulka и codium.desktop сохранится в файл ~/.config/mimeapps.list и при последующих кликах по файлу он всегда будет открываться с помощью VSCodium. Теперь все программы, которые используют shared-mime-info, будут открывать файлы с расширеним .bulka с помощью VSCodium.
Файлы mimeinfo.cache создаются и обновляются утилитой update-desktop-database.
Утилита update-desktop-database сканирует все .desktop-файлы в каталогах ~/.local/share/applications/ и /usr/share/applications/ и собирает из них информацию о том, какие программы заявляют поддержку каких MIME-типов. И она записывает эту информацию в файл mimeinfo.cache в том же каталоге.
То есть, mimeinfo.cache - это ускоритель. Программа каждый раз не лазит по всем .desktop-файлам, она заглядывает в mimeinfo.cache.
Допустим, у меня есть файл leafpad.desktop, в котором написано:
# /usr/share/applications/leafpad.desktop
[Desktop Entry]
Name=Leafpad
Exec=leafpad %f
MimeType=text/plain;text/x-bulka;
Я запускаю команду:
update-desktop-database ~/.local/share/applications
# ~/.local/share/applications/mimeinfo.cache
[MIME Cache]
text/plain=leafpad.desktop;
text/x-bulka=leafpad.desktop;
Обычно update-desktop-database не вызывают вручную. Обычно её вызывает пакетный менеджер автоматически после установки новой программы. Только если я вручную добавил новый .desktop-файл или изменил "MimeType=" в существующем, имеет смысл вызвать update-desktop-database. Или при добавлении нового MIME-типа.
То есть, чтобы программа официально стала поддерживать мой тип, я должен вручную открыть её .desktop-файл, добавить туда свой тип и запустить команду: update-desktop-database ~/.local/share/applications, чтобы обновить mimeinfo.cache.
MimeType=text/plain;inode/directory;text/x-bulka;
Может, можно как-то сделать, чтобы утилита file тоже определяла наш файл не просто как "text/plain", а как "application/x-bulka"? Чтобы быть уверенным, что любая программа определит наш тип точно.
Это сделать можно. Но поскольку file читает содержимое файла, чтобы определить его тип, нам нужно добавить внутрь файла какой-то опознавательный знак - что-то, что будет отличать наш тип файла, от обычного текстового. Что-то типа сигнатуры. Допустим, пусть у нас файл начинается со строки "BULKA!".
# содержимое моего файла myfile.bulka:
BULKA!
Первая строка, которую мы вставляем в файл.
Вторая строка.
Теперь нам надо указать утилите file, что все текстовые файлы, которые начинаются со строки "BULKA!", относятся к типу "application/x-bulka".
# файл ~/.magic:
0 string BULKA! application/x-bulka
!:mime application/x-bulka
А вот пример файла ~/.magic с несколькими типами:
# файл ~/.magic:
0 string FOO! application/x-foobar
!:mime application/x-foobar
0 string KAKA! application/x-kaka
!:mime application/x-kaka
0 string KAKA2! application/x-kaka2
!:mime application/x-kaka2
0 string BULKA! application/x-bulka
!:mime application/x-bulka
Теперь мы можем проверить тип нашего файла с помощью file:
file --mime-type myfile.bulka
# myfile.bulka: application/x-bulka
file определяет только по содержимому. А философия Freedesktop MIME: определять только по расширению, а не по магии!!!
shared-mime-info - это стандарт Freedesktop и набор файлов, которые описывают все известные MIME-типы для Linux (и других Unix-подобных систем).
# Системные MIME-типы:
/usr/share/mime/
# Пользовательские MIME-типы:
~/.local/share/mime/
Программы, которые используют shared-mime-info, тоже могут определять тип файла по его содержимому. Для этого в XML-файлах предусмотрена директива <magic>. Давайте подправим наш файл application-x-bulochka.xml таким образом, чтобы shared-info-mime-программы прочитывали из первой строки файла нашу сигнатуру "BULKA!".
<!-- ~/.local/share/mime/packages/application-x-bulochka.xml -->
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-bulka">
<magic priority="80">
<match type="string" value="BULKA!" offset="0"/>
</magic>
<!-- Распознавание файла по расширению я удалил -->
</mime-type>
</mime-info>
После того, как мы изменили содержимое XML-файла, запустим команду:
update-mime-database ~/.local/share/mime
Теперь давайте попробуем определить тип файла myfile.bulka с помощью gio:
# Если у нас в файле первая строка: "BULKA!":
gio info myfile.bulka | grep content-type
# standard::content-type: application/x-bulka
# standard::fast-content-type: application/octet-stream
# Если мы убираем "BULKA!" из первой строки:
gio info myfile.bulka | grep content-type
# standard::content-type: text/plain
# standard::fast-content-type: application/octet-stream
То есть теперь gio определяет тип файла на основе его содержимого.
А теперь давайте усложним XML-правило. <match> может быть и вложенным.
<!-- ~/.local/share/mime/packages/application-x-bulochka.xml -->
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-bulka">
<magic priority="80">
<match type="string" value="BULKA!" offset="0">
<match type="string" value="V2" offset="6"/>
</match>
</magic>
<!-- Распознавание файла по расширению я удалил -->
</mime-type>
</mime-info>
Теперь gio будет определять тип файла как "application/x-bulka" только если первая строка равна: "BULKA!V2".
Ещё пример XML-файла. То же самое, что и в предыдущем примере, но только здесь мы добавили распознавание файла по расширению. Сначал идёт проверка по содержимому. Если "BULKA!" и "V2" не совпали, то тогда мы смотрим на расширение файла. Если оно равно: ".bulka", то тогда тип нашего файла всё равно: "application/x-bulka".
<!-- ~/.local/share/mime/packages/application-x-bulochka.xml -->
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-bulka">
<magic priority="80">
<match type="string" value="BULKA!" offset="0">
<match type="string" value="V2" offset="6"/>
</match>
</magic>
<!-- Здесь <glob> находится ПОСЛЕ <magic> -->
<glob pattern="*.bulka"/>
</mime-type>
</mime-info>
В следующем XML-фрагменте мы переместили <glob> НАД <magic>, чтобы продемонстрировать, что порядок написания правил не влияет на приоритет. Приоритет всегда у MAGIC. Сначала проверяется содержимое файла, а потом расширение.
<!-- ~/.local/share/mime/packages/application-x-bulochka.xml -->
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="application/x-bulka">
<!-- Тут мы переместили <glob> выше - НАД <magic> -->
<glob pattern="*.bulka"/>
<magic priority="80">
<match type="string" value="BULKA!" offset="0">
<match type="string" value="V2" offset="6"/>
</match>
</magic>
</mime-type>
</mime-info>
Аттрибут "priority" указывает приоритет данного правила. Например, у PNG-файлов (MIME-тип: image/png) в системе по умолчанию приоритет "50". Если я хочу создать своё правило для image/png, то мне нужно указать приоритет выше "50" и тогда моё правило переопределит системное.
Давайте посмотрим на пример XML-файла для image/png, который приведён на сайте freedesktop.org:
<?xml version="1.0" encoding="UTF-8"?>
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
<mime-type type="image/png">
<comment xml:lang="en">PNG image</comment>
<comment xml:lang="af">png beeld</comment>
...
<magic priority="50">
<match type="string" value="\x89PNG" offset="0"/>
</magic>
<glob pattern="*.png"/>
</mime-type>
</mime-info>
Здесь "\x89PNG" - это сигнатура первых байтов для формата PNG.