ホームディレクトリのパスを取得するには
ログインしているユーザーのホームディレクトリのパスを各言語で取得する基本形を示す。
ファイル API は ~ を展開しない点と、環境変数 HOME を優先する言語・無視する言語・実在しない場所を指すと空文字を返す言語があることまでを扱う。
公開:
選んだ言語は、ほかのサンプルコードを開いたときも最初に表示する。
Python
import os
from pathlib import Path
print(Path.home()) # /home/taro
print(Path.home() / ".config") # /home/taro/.config
print(os.path.expanduser("~/.config")) # /home/taro/.config
print(os.path.expanduser("~root")) # /root
# ファイル API は ~ を展開しない
print(os.path.exists("~")) # False
print(os.path.expanduser("a/~/b")) # a/~/b(先頭の ~ だけが対象)
try:
open("~/memo.txt", "w")
except FileNotFoundError as e:
print(e) # [Errno 2] No such file or directory: '~/memo.txt'Path.home() は環境変数 HOME を優先し、無ければ passwd のホームを返す。
文字列の ~ を展開するのは os.path.expanduser で、先頭の ~ だけが対象になり、a/~/b のように途中にある ~ は変えない。open("~/memo.txt") のようにファイル API へ直接渡しても展開されず、FileNotFoundError になる。
JavaScript
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
console.log(os.homedir()); // /home/taro
console.log(path.join(os.homedir(), ".config")); // /home/taro/.config
// ファイル API は ~ を展開しない
console.log(fs.existsSync("~")); // false
// 展開が必要なら、先頭の ~ だけを自前で置き換える
function expandHome(p) {
if (p === "~" || p.startsWith("~/")) return path.join(os.homedir(), p.slice(1));
return p;
}
console.log(expandHome("~/.config")); // /home/taro/.config
console.log(expandHome("a/~/b")); // a/~/bos.homedir() は環境変数 HOME を優先し、無ければ passwd のホームを返す。fs などのファイル API は ~ を展開しないので、展開が必要なら先頭の ~ だけを自前で置き換える。p.startsWith("~/") で判定するため、a/~/b のように途中にある ~ は変わらない。
TypeScript
import os from "node:os";
import path from "node:path";
function expandHome(p: string, home: string = os.homedir()): string {
if (p === "~") return home;
if (p.startsWith("~/")) return path.join(home, p.slice(2));
return p;
}
console.log(expandHome("~")); // /home/taro
console.log(expandHome("~/.config")); // /home/taro/.config
console.log(expandHome("~root/.ssh")); // ~root/.ssh(~ユーザー名 は展開しない)
console.log(expandHome("a/~/b")); // a/~/b
console.log(expandHome("~/.config", "/srv/app")); // /srv/app/.config(home を渡せばテストしやすい)展開する関数が home を引数に取り、既定値に os.homedir() を渡している。
テストでは別のホームを渡せるので、実行する環境のホームに結果が左右されない。~root/.ssh のような ~ユーザー名 の形は展開せずそのまま返すので、必要になったら別に扱う。
Go
package main
import (
"fmt"
"os"
"os/user"
"path/filepath"
)
func main() {
home, err := os.UserHomeDir()
if err != nil {
panic(err)
}
fmt.Println(home) // /home/taro
fmt.Println(filepath.Join(home, ".config")) // /home/taro/.config
// os/user は passwd を引くので、HOME とは別の経路で求める
u, err := user.Current()
if err != nil {
panic(err)
}
fmt.Println(u.HomeDir) // /home/taro
// ファイル API は ~ を展開しない
_, err = os.Stat("~")
fmt.Println(os.IsNotExist(err)) // true
}os.UserHomeDir は Unix で環境変数 HOME だけを読み、未設定または空のときはエラーを返す。
passwd を引く user.Current() の HomeDir は HOME を見ないので、HOME を書き換えても /home/taro のままになる。
ファイル API の os.Stat("~") は展開せず、~ という名前を探して存在しないエラーを返す。
Rust
use std::env;
use std::path::Path;
fn main() {
let home = env::home_dir().expect("home directory not found");
println!("{}", home.display()); // /home/taro
println!("{}", home.join(".config").display()); // /home/taro/.config
// ファイル API は ~ を展開しない
println!("{}", Path::new("~").exists()); // false
}env::home_dir() は HOME を優先し、無ければ passwd のホームを返す。
戻り値が Option<PathBuf> なので、サンプルでは取れなかったときに expect で止めている。
ファイル API の Path::exists は ~ を展開せず、~ という名前を探すだけなので false になる。
Java
import java.nio.file.Files;
import java.nio.file.Path;
public class Main {
public static void main(String[] args) {
Path home = Path.of(System.getProperty("user.home"));
System.out.println(home); // /home/taro
System.out.println(home.resolve(".config")); // /home/taro/.config
// ファイル API は ~ を展開しない
System.out.println(Files.exists(Path.of("~"))); // false
}
}System.getProperty("user.home") は環境変数 HOME を見ず、passwd のホームを返す。HOME=/tmp/other を付けて実行しても /home/taro のままだった。
差し替えたいときは -Duser.home=/tmp/other のようにシステムプロパティで渡す。Files.exists(Path.of("~")) は ~ を展開せず false になる。
C#
using System;
using System.IO;
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
Console.WriteLine(home); // /home/taro
Console.WriteLine(Path.Combine(home, ".config")); // /home/taro/.config
// 既定では、フォルダが実在しないと空文字が返る。パスだけが欲しいときは DoNotVerify を渡す
string raw = Environment.GetFolderPath(
Environment.SpecialFolder.UserProfile, Environment.SpecialFolderOption.DoNotVerify);
Console.WriteLine(raw == home); // True
// ファイル API は ~ を展開しない
Console.WriteLine(Directory.Exists("~")); // FalseEnvironment.GetFolderPath(Environment.SpecialFolder.UserProfile) は Linux で HOME を優先し、無ければ passwd のホームを返す。
既定ではそのフォルダが実在しないと空文字を返すので、パスだけが欲しいときは SpecialFolderOption.DoNotVerify を渡す。
ファイル API の Directory.Exists("~") は展開せず False になる。
~ を展開するのはシェルで、ファイル API ではない
~ を「ホームディレクトリ」として扱うのはシェルの機能であり、ファイル API は ~ をただの文字として受け取る。
Python で open("~/memo.txt", "w") を実行すると、~ という名前のディレクトリが無いので FileNotFoundError になる。
作る側はもっと厄介で、Python の os.makedirs("~/data") や Node.js の fs.mkdirSync("~/data", { recursive: true }) は、エラーにならずカレントディレクトリの下に ~ という名前のディレクトリを作る(どちらも実行して確かめた)。
設定ファイルや引数で受け取った ~/data のような文字列は、ファイル API に渡す前に展開する。
Python には展開用の os.path.expanduser があり、先頭の ~ だけが対象になる。
JavaScript と TypeScript のサンプルは、ホームを求めたうえで先頭の ~ を自前で置き換えている。
環境変数 HOME を見る言語と見ない言語
Linux で、passwd 上のホームが /home/taro のユーザーに HOME=/tmp/other を付けて実行し、ホームを求める 1 行の結果を比べた(/tmp/other は実在しないディレクトリである)。
- Python の
Path.home():/tmp/other - Node.js の
os.homedir():/tmp/other - Go の
os.UserHomeDir():/tmp/other(user.Current().HomeDirは/home/taroのまま) - Rust の
env::home_dir():/tmp/other - Java の
user.home:/home/taro(HOMEを見ない) - C# の
GetFolderPath: 空文字(SpecialFolderOption.DoNotVerifyを渡すと/tmp/other)
Java だけが HOME を見ずに passwd のホームを返すので、HOME を一時ディレクトリに差し替えて試すテストでは、Java だけが本物のホームを読み書きしてしまう。
差し替えたいときは -Duser.home=/tmp/other のようにシステムプロパティで渡す。
C# は HOME を優先するものの、既定ではそのフォルダが実在しないと空文字を返す。
空文字が返る場合に備えて、DoNotVerify を付けるか、空文字を弾いてから使う。
HOME が未設定のとき・空のとき
env -u HOME で HOME を未設定にして同じ 1 行を実行すると、Python・Node.js・Rust・Java・C# は passwd のホームである /home/taro を返す。
Go の os.UserHomeDir() だけは $HOME is not defined というエラーを返す。
HOME= で空文字にすると結果が割れる。
Python の Path.home() は /、Node.js の os.homedir() は空文字、Go は未設定のときと同じエラーになり、Rust・Java・C# は /home/taro を返す。
空文字や / を受け取ったまま .config を結合すると、意図しない場所を指すパスができる。
Go では err を、Node.js では空文字を確認してから使う。
Windows では USERPROFILE を見る
Windows 11 で HOME に別のパスを設定して試したところ、Python の Path.home() と Node.js の os.homedir() はどちらも USERPROFILE の値を返し、HOME は見なかった。
Linux の感覚で HOME を書き換えてホームを差し替えるテストは、Windows では効かない。
Go・Rust・Java・C# の Windows での挙動は確かめていないので、Windows で使うときは各言語のドキュメントで確認してほしい。
出力例について
コード中のコメントは、Docker の公式イメージ(Python 3.14、Node.js 22、Go 1.27、Rust 1.99、OpenJDK 21、.NET SDK 8.0)の Linux で、ホームが /home/taro の一般ユーザーとして 2026-10-19 に実行した結果である。
root で実行するとホームは /root になる。
TypeScript は Node.js 22 上で tsx を使って実行した。