2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Linux] カレントユーザのホームディレクトリはどこから取得すべきか

Last updated at Posted at 2021-09-04

(※タイトルの通り、本記事では Linux のみを対象として調査しています。Unix/非 Unix 問わず、それ以外の OS には当てはまらない部分が多いのでご注意ください)

答え

$HOME 環境変数を見ましょう。

Linux でのユーザ情報は「パスワードデータベース」(通常は /etc/passwd ファイル。以下、本記事では単に "passwd" と略称)と呼ばれるところに格納されており、そこにユーザごとのホームディレクトリ情報も記載されています。

しかし passwd の情報を取得する getpwuid (getpwnam) の MAN ページに下記の通り記載があります:

アプリケーションが、ユーザーのホームディレクトリを決定する場合には、 (getpwuid(getuid())->pw_dir の値ではなく) HOME の値を検査するようにすべきである。 なぜなら、このようにすることで、ユーザーがログインセッション中で 「ホームディレクトリ」の意味を変更できるようになるからである。

最近、コンテナを実行する際に $HOME を上書きしている例をちらほら見かけたのですが、

  • 値を設定するユーザとしてアプリケーションがその値をちゃんと見てくれると期待していいものなのか?(環境変数無視して別の値を見に行ってしまうアプリケーションはないのか?)
  • アプリケーション開発者として $HOME の値を信頼してもいいものなのか?

ともやもやしていました。

結論、どちらも YES。ユーザは $HOME を上書きしていいし、アプリケーションはその値を正しい「ホームディレクトリ」として扱うべきということで、すっきりしました。

補遺 1: 誰が(最初に)$HOME を設定するのか

答え: システム(OS)のログイン処理が passwd に記載の値を設定する。

これまた先ほど引用した man getpwnam の一つ前の文に記載があります:

ログインプロセスは、このフィールドの値を使って、 ログインシェルの HOME 環境変数を初期化する。

また POSIX にも下記のような規定があります:

The system shall initialize this variable at the time of login to be a pathname of the user's home directory. See <pwd.h>.

(拙訳)
システムが、ログイン時にこの変数をユーザのホームディレクトリのパス名に設定すべきである。<pwd.h> 参照。

参考:

補遺 2: 世の実装はどうなっているのか?

ここからは完全に趣味の領域で長くなります。結びの言葉もありませんので、興味がない方はここでそっといただけると。

いくつかの言語のライブラリやアプリケーションで、ホームディレクトリの取得処理がどうなっているのかを実際に調査してみました。

基本的には「$HOME 環境変数を見る、なければ passwd を見る」という処理が多いようですが、前者だけで済ませている例もありました。

また、空文字の環境変数が設定されていたときの挙動についても、実装により差が見受けられます。差異にも注意が必要ですが、それ以上に空文字がそのまま使われるシステムでは ~/.config/.config に展開されるということなので気をつける必要があります。

言語・ライブラリ 環境変数 空文字の環境変数 passwd その他のフォールバック
Python os.path 使われる
Ruby Dir 使われる
Node.js os 使われる
Go os 使われない :x:
Go os/user :x:
Go github.com/mitchellh/go-homedir 使われない sh -c 'cd && pwd'
Rust dirs/directories (dirs-sys) 使われない
Rust home/std::env 使われる
Rust home/std::env 使われる
Bash ~ の展開 使われる
Bash 引数なし cd 使われる :x:
Ash ~ の展開・引数なし cd 使われる :x:
Git 使われる :x:

Python: os.path.expanduser('~')

環境変数と passwd の両方を見ます。空文字でも設定されていればその値が使われます。

Ruby: Dir.home

環境変数と passwd の両方を見ます。空文字でも設定されていればその値が使われます。

Node.js: os.homedir()

環境変数と passwd の両方を見ます。空文字でも設定されていればその値が使われます。

Go

os.UserHomeDir()

環境変数のみを見ます。空文字は未設定と同じ扱いになります。

os/user

os.UserHomeDir() は Go 1.12 で追加されたものですが、それ以前に標準パッケージを使う場合は os/user パッケージが利用されていたようです

ただしこれはあくまで passwd を扱うためのライブラリなので環境変数は特に見ませんし、CGO 必須でクロスコンパイルに向いていませんでした。

github.com/mitchellh/go-homedir

というわけで Go 1.12 以前では github.com/mitchellh/go-homedir パッケージが人気でした(ちなみに @mitchellh さんは Hashicorp の創業者であり、Terraform や Vagrant などを作った方ですね!)。

これは環境変数と passwd の両方を見るだけでなく、それでもダメなら sh -c 'cd && pwd' を実行するという処理が入っているのがユニークです(結局 cd コマンドは何を見るの?となるので、果たしてこのフォールバックに意味があるのかはよくわかりません)。空文字は未設定と同じ扱いになります。

Rust

Rust は std::env::home_dir() という関数があるのですが、Windows 上での動作に問題があるとのことで現在は deprecated になっています。ですので、いくつか thrid-party の crate も合わせて確認してみましょう。

dirs/directories (dirs-sys)

dirsdirectories が一般開発者に使われるライブラリで、実際の処理が内部用ライブラリ dirs-sys にて実装されています。

環境変数と passwd の両方を見ます。空文字は未設定と同じ扱いになります。

home/std::env

homeCargo などで使われている crate で、Unix 環境では単に公式の std::env::home_dir() を使う実装になっています(前述の通り std::env::home_dir が deprected なのは Windows 上での動作が問題になったのであり、Unix では問題なく動作する)。

環境変数と passwd の両方を見ます。空文字でも設定されていればその値が使われます(home crate のドキュメント上は「空文字は未設定扱い」となっていますが、間違っているようです。ref: brson/home#22)。

Bash

~ の展開

環境変数と passwd の両方を見ます。空文字でも設定されていればその値が使われます。

引数なし cd

環境変数のみを見ます。空文字でも設定されていればその値が使われますが、それはつまり移動しないということです。

Busybox Ash

~ の展開、引数なし cd のいずれでも環境変数しか見ません。空文字でも設定されていればその値が使われます。

Git

環境変数のみを見ます(git config --global$XDG_CONFIG_HOME も見ます)。空文字でも設定されていればその値が使われます。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?