いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-51 (XDG Base Directory 仕様の取り扱い (xdgパッケージ))

概要

最近は、いろんなアプリが XDG Base Directory の仕様に則った動きをするようになってますね。

XDGってなんぞや?って方もいらっしゃると思いますが、

specifications.freedesktop.org

簡単にいうと、デスクトップ環境で使う標準的なディレクトリ構造を規定している仕様です。

アプリケーションのデータファイルはここに置くべきとか、設定ファイルはここに置くべきとかが決まっています。

基本的には、環境変数で設定しておくものらしいんですが、設定していない場合のデフォルト値が決まっているので

ほとんどの人は設定しないんじゃないですかね。

例えば、Linux系のOSの場合は、$XDG_CONFIG_HOME の値が ~/.config になります。このディレクトリ、いつの間にか出来てて、なにこれ?ってなる人多いんじゃないでしょうか。

で、Goからこれらの値を利用してみたいってときに、以下の便利なライブラリがありました。

github.com

こういうライブラリ、すごく助かります。

てことで、ちょっと試してみました。

サンプル

XDG Base Directory のサンプル

package xdgspec

import (
    "github.com/adrg/xdg"
    "github.com/devlights/try-golang/lib/output"
    "runtime"
)

// XdgBaseDirectory は、XDG Base Directory についてのサンプルです.
// [xdg](https://github.com/adrg/xdg) を利用して各値を取得しています.
//
// REFEFENCES::
//   - https://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html
//   - https://kledgeb.blogspot.com/2013/04/ubuntu-10-xdg-base-directory.html
func XdgBaseDirectory() error {
    // ------------------------------------------------------------
    // XDG Base Directory について
    // XDG の各値は、xdg パッケージを利用すると簡単に利用できる
    // このパッケージは、Windows/MacOS/Unix に対応している
    //
    // xdg パッケージは、対象となる環境変数が設定されていれば、その値を返し
    // 設定されていない場合は、デフォルトの値を返すようになっている。
    // ------------------------------------------------------------
    output.Stdoutl("[OS]", runtime.GOOS)

    // XDG_DATA_HOME
    // ユーザ個別のデータファイルが書き込まれる場所 (ユーザディレクトリ)
    output.Stdoutl("XDG_DATA_HOME", xdg.DataHome)

    // XDG_CONFIG_HOME
    // ユーザ個別の設定が書き込まれる場所 (ユーザディレクトリ)
    output.Stdoutl("XDG_CONFIG_HOME", xdg.ConfigHome)

    // XDG_CACHE_HOME
    // ユーザ毎のキャッシュデータの置き場 (ユーザディレクトリ)
    output.Stdoutl("XDG_CACHE_HOME", xdg.CacheHome)

    // XDG_RUNTIME_DIR
    // ユーザ毎の実行時ファイルやその他のファイルを置くべき場所 (ユーザディレクトリ)
    output.Stdoutl("XDG_RUNTIME_DIR", xdg.RuntimeDir)

    // XDG_DATA_DIRS
    // データファイルを検索する際のサーチパス (システムディレクトリ)
    //
    // (補足) アプリのデータを検索する場合は $XDG_DATA_HOME:$XDG_DATA_DIRS の順で行い
    // ユーザ毎のデータを優先させる
    output.Stdoutl("XDG_DATA_DIRS", xdg.DataDirs)

    // XDG_CONFIG_DIRS
    // 設定ファイルを検索する際のサーチパス (システムディレクトリ)
    //
    // (補足) アプリの設定を検索する場合は $XDG_CONFIG_HOME:$XDG_CONFIG_DIRS の順で行い
    // ユーザ毎の設定を優先させる
    output.Stdoutl("XDG_CONFIG_DIRS", xdg.ConfigDirs)

    return nil
}

try-golang/xdg_base_directory.go at master · devlights/try-golang · GitHub

実行すると以下のようになります。

ENTER EXAMPLE NAME: xdg_base_directory
[Name] "xdg_base_directory"
[OS]                 darwin
XDG_DATA_HOME        /Users/xxxx/Library/Application Support
XDG_CONFIG_HOME      /Users/xxxx/Library/Preferences
XDG_CACHE_HOME       /Users/xxxx/Library/Caches
XDG_RUNTIME_DIR      /Users/xxxx/Library/Application Support
XDG_DATA_DIRS        [/Library/Application Support]
XDG_CONFIG_DIRS      [/Library/Preferences]

MacOSだと、$XDG_CONFIG_HOME の値が ~/.config じゃないんですね。

~/Library/Preferences の下は plist ファイルばっかりなので、ここは ~/.config が良かったけどXDG自体がデスクトップ環境のためのディレクトリ仕様なので、こうなるのは納得。

ついでに、Linuxでも実行してみた。

ENTER EXAMPLE NAME: xdg_base_directory
[Name] "xdg_base_directory"
[OS]                 linux
XDG_DATA_HOME        /home/gitpod/.local/share
XDG_CONFIG_HOME      /home/gitpod/.config
XDG_CACHE_HOME       /home/gitpod/.cache
XDG_RUNTIME_DIR      /run/user/33333
XDG_DATA_DIRS        [/usr/local/share /usr/share]
XDG_CONFIG_DIRS      [/etc/xdg]

XDG User Directory のサンプル

package xdgspec

import (
    "github.com/adrg/xdg"
    "github.com/devlights/try-golang/lib/output"
    "runtime"
)

// XdgUserDirectory は、XDG User Directory についてのサンプルです.
// [xdg](https://github.com/adrg/xdg) を利用して各値を取得しています.
//
// REFEFENCES::
//   - https://www.freedesktop.org/wiki/Software/xdg-user-dirs/
func XdgUserDirectory() error {
    // ------------------------------------------------------------
    // XDG User Directory について
    // XDG の各値は、xdg パッケージを利用すると簡単に利用できる
    // このパッケージは、Windows/MacOS/Unix に対応している
    //
    // XDG User Directory は、Documents,Downloads,Music,Desktopなどの
    // $HOME に配置されるユーザ共通のディレクトリのセットのこと
    //
    // xdg パッケージは、対象となる環境変数が設定されていれば、その値を返し
    // 設定されていない場合は、デフォルトの値を返すようになっている。
    // ------------------------------------------------------------
    output.Stdoutl("[OS]", runtime.GOOS)

    // XDG_DESKTOP_DIR
    // デスクトップの場所
    output.Stdoutl("XDG_DESKTOP_DIR", xdg.UserDirs.Desktop)

    // XDG_DOWNLOAD_DIR
    // ダウンロードの場所
    output.Stdoutl("XDG_DOWNLOAD_DIR", xdg.UserDirs.Download)

    // XDG_DOCUMENTS_DIR
    // ドキュメントの場所
    output.Stdoutl("XDG_DOCUMENTS_DIR", xdg.UserDirs.Documents)

    // XDGの標準規定にはないもの
    output.Stdoutl("Application dirs", xdg.ApplicationDirs)
    output.Stdoutl("Font dirs", xdg.FontDirs)

    return nil
}

try-golang/xdg_user_directory.go at master · devlights/try-golang · GitHub

実行すると以下のようになります。

ENTER EXAMPLE NAME: xdg_user_directory
[Name] "xdg_user_directory"
[OS]                 darwin
XDG_DESKTOP_DIR      /Users/xxxx/Desktop
XDG_DOWNLOAD_DIR     /Users/xxxx/Downloads
XDG_DOCUMENTS_DIR    /Users/xxxx/Documents
Application dirs     [/Applications]
Font dirs            [/Users/xxxx/Library/Fonts /Library/Fonts /System/Library/Fonts /Network/Library/Fonts]

ついでに Linux で実行してみた

ENTER EXAMPLE NAME: xdg_user_directory
[Name] "xdg_user_directory"
[OS]                 linux
XDG_DESKTOP_DIR      /home/gitpod/Desktop
XDG_DOWNLOAD_DIR     /home/gitpod/Downloads
XDG_DOCUMENTS_DIR    /home/gitpod/Documents
Application dirs     [/home/gitpod/.local/share/applications /usr/local/share/applications /usr/share/applications]
Font dirs            [/home/gitpod/.local/share/fonts /home/gitpod/.fonts /usr/local/share/fonts /usr/share/fonts]

xdgパッケージの便利な関数のサンプル

package xdgspec

import (
    "github.com/adrg/xdg"
    "github.com/devlights/try-golang/lib/output"
    "io/ioutil"
    "os"
    "path/filepath"
    "runtime"
)

// XdgFileOperation は、[xdg](https://github.com/adrg/xdg) を利用して
// XDGの規定に従った場所にファイルを配置したり検索したりしています.
func XdgFileOperation() error {
    var (
        dataDir = filepath.Join(xdg.DataHome, "try-golang")
    )

    output.Stdoutl("[OS]", runtime.GOOS)
    output.Stdoutl("[dataDir]", dataDir)

    // 処理する前に既にファイルが存在してたら消す
    if _, err := os.Stat(dataDir); err == nil {
        output.Stdoutl("[exists?]", "存在する --> 削除")

        if err = os.RemoveAll(dataDir); err != nil {
            output.Stderrl("[os.Remove]", err)
            return err
        }
    } else {
        output.Stdoutl("[exists?]", "存在しない")
    }

    // xdg.DataFile() に アプリ名/ファイル名 で渡すとXDGの規定に従ったパスを
    // 生成して返してくれる. xdg.DataFile() にすると $XDG_DATA_HOME
    // xdg.ConfigFile() にすると $XDG_CONFIG_HOME がベースとなる
    //
    // このとき、ファイルの親ディレクトリ(つまりアプリ名の部分)が
    // 存在しない場合は、ディレクトリを作成してくれる
    dataFile, err := xdg.DataFile("try-golang/mydata.txt")
    if err != nil {
        output.Stderrl("[xdg.DataFile]", err)
        return err
    }

    output.Stdoutl("[xdg.DataFile]", dataFile)

    if _, err = os.Stat(dataDir); err == nil {
        output.Stdoutl("[exists?]", "存在する")
    } else {
        output.Stdoutl("[exists?]", "存在しない")
    }

    if err = ioutil.WriteFile(dataFile, []byte("helloworld\n"), 0644); err != nil {
        output.Stderrl("[ioutil.WriteFile]", err)
        return err
    }

    if bytes, err := ioutil.ReadFile(dataFile); err == nil {
        output.Stdoutl("[ioutil.ReadFile]", string(bytes))
    }

    // xdg.SearchDataFile() を利用すると、指定した アプリ名/ファイル名 を探してくれる.
    // 存在しない場合は、err に値が入る.
    // xdg.DataFile() と違い、こちらは親ディレクトリを作ったりはしてくれない.
    // 既にファイルが存在する場合に利用する.
    dataFile2, err := xdg.SearchDataFile("try-golang/mydata.txt")
    if err != nil {
        output.Stderrl("[xdg.SearchDataFile]", err)
        return err
    }

    output.Stdoutl("[xdg.SearchDataFile]", dataFile2)

    // 後始末
    _ = os.RemoveAll(dataDir)

    return nil
}

try-golang/xdg_file_operation.go at master · devlights/try-golang · GitHub

実行すると以下のようになります。

ENTER EXAMPLE NAME: xdg_file_operation
[Name] "xdg_file_operation"
[OS]                 darwin
[dataDir]            /Users/xxxx/Library/Application Support/try-golang
[exists?]            存在しない
[xdg.DataFile]       /Users/xxxx/Library/Application Support/try-golang/mydata.txt
[exists?]            存在する
[ioutil.ReadFile]    helloworld

[xdg.SearchDataFile] /Users/xxxx/Library/Application Support/try-golang/mydata.txt

ついでに Linux で実行してみた

ENTER EXAMPLE NAME: xdg_file_operation
[Name] "xdg_file_operation"
[OS]                 linux
[dataDir]            /home/gitpod/.local/share/try-golang
[exists?]            存在しない
[xdg.DataFile]       /home/gitpod/.local/share/try-golang/mydata.txt
[exists?]            存在する
[ioutil.ReadFile]    helloworld

[xdg.SearchDataFile] /home/gitpod/.local/share/try-golang/mydata.txt

過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com