「cannot set LC_ALL」ロケール警告の解決

「cannot set LC_ALL」ロケール警告の解決

この記事で解決できること

  • cannot set LC_ALL / setlocale 警告が なぜ出るのか が分かる
  • どの環境変数が原因かを locale / locale -a即切り分け できる
  • 警告を 応急処置で止める方法 と、ロケールを生成して 恒久解決する手順 が身につく

結論(切り分けの型)

  • 警告の正体は「要求したロケールがシステムに存在しない」の 1 点
  • まず locale で警告を、locale -a手元にあるロケール を確認する
  • 無ければ locale-gen で生成、急ぐなら export LC_ALL=C で黙らせる

前提(対象環境)

  • OS: Ubuntu / Debian / RHEL 系(glibc + systemd)
  • ローカル端末・SSH 接続先サーバ・コンテナのいずれでも発生しうる

「cannot set LC_ALL」警告はなぜ出るのか?

結論: LANG / LC_* 環境変数が指すロケール(例 en_US.UTF-8)が、そのシステムに生成・インストールされていないため。存在しないロケールを要求され、glibc が C ロケールへフォールバックする。

ロケールは「言語・文字コード・日付や数値の書式」をまとめた設定で、en_US.UTF-8ja_JP.UTF-8 のような名前を持つ。プログラムは起動時に setlocale() を呼び、LC_ALLLC_*LANG の優先順で要求するロケールを決める。

このとき要求した名前がシステムに存在しないと、glibc は警告を出して安全な C(POSIX)ロケールにフォールバックする。典型的なメッセージは次の通り。

# locale コマンド自身
locale: Cannot set LC_ALL to default locale: No such file or directory

# bash / 各種コマンド
bash: warning: setlocale: LC_ALL: cannot change locale (en_US.UTF-8): No such file or directory

# perl(apt や Git のフックで頻出)
perl: warning: Setting locale failed.
perl: warning: Please check that your locale settings:
	LC_ALL = (unset),
	LC_CTYPE = "UTF-8",
	LANG = "en_US.UTF-8"
    are supported and installed on your system.
perl: warning: Falling back to the standard locale ("C").

「存在しない」の主なパターン

  • locales パッケージ自体が入っていない(最小構成のコンテナ・クラウドイメージ)
  • パッケージは入っているが、そのロケールが生成されていないlocale-gen 未実行)
  • SSH で手元の端末のロケールが転送され、接続先サーバに同じロケールが無い
  • タイプミス(en_US.utf8en_US.UTF-8 等)で存在しない名前を指している

どの環境変数がずれているかをどう確認するか

結論: locale で現在値と警告対象の変数を、locale -a で利用可能なロケール一覧を確認する。要求している名前が一覧に無ければ、それが原因。

まず locale を実行すると、現在の設定と「どの変数が問題か」が同時に分かる。

$ locale
locale: Cannot set LC_ALL to default locale: No such file or directory
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
...
LC_ALL=

次に、そのシステムで実際に使えるロケールを一覧する。

$ locale -a
C
C.UTF-8
POSIX

この例では en_US.UTF-8 を要求しているのに、一覧には C 系しか無い。要求名が locale -a に出てこないことが、警告の決定的な証拠になる。

名前の表記ゆれに注意。locale -aen_US.utf8(小文字・ハイフン無し)形式で表示することがあるが、LANG=en_US.UTF-8 形式の指定でも同一として扱われる。一方で en_US(文字コード無し)と en_US.UTF-8別物なので、サフィックスまで一致しているか確認する。

SSH 接続で警告が出るのはなぜか

結論: SSH クライアントが SendEnv LANG LC_* で手元のロケールをサーバへ転送し、サーバ側が AcceptEnv で受け入れるため。サーバにそのロケールが無いと、ログインのたびに警告が出る。

「ローカルでは出ないのにサーバに SSH すると毎回出る」場合、原因はほぼこれ。多くのディストリの SSH クライアント設定には次の行がある。

# /etc/ssh/ssh_config もしくは ~/.ssh/config
SendEnv LANG LC_*

これにより手元の LANG=ja_JP.UTF-8 などがサーバへ送られる。サーバ側 /etc/ssh/sshd_configAcceptEnv LANG LC_* が受け取り、セッションの環境変数に設定する。サーバにそのロケールが無ければ、シェル起動時に setlocale が失敗する。

対処は 2 択。サーバ側にロケールを入れる(後述の生成手順)か、転送をやめる。転送を止めるならクライアント側の設定を書き換える。

# ~/.ssh/config で特定ホストだけ転送を無効化
Host myserver
    SendEnv -LANG -LC_*

特定の接続だけ一時的に転送を止めたいなら、ssh-o で上書きできる。

$ ssh -o SendEnv= user@myserver

ロケールを生成・インストールするには?(Debian / Ubuntu)

結論: locales パッケージを入れ、locale-gen で目的のロケールを生成し、update-locale で既定値を設定する。対話式の dpkg-reconfigure locales でも同じことができる。

Debian / Ubuntu ではロケールは「パッケージ導入」と「生成」の 2 段階。

# 1. locales パッケージ(未導入の最小環境向け)
$ sudo apt update && sudo apt install -y locales

# 2. 目的のロケールを生成
$ sudo locale-gen en_US.UTF-8 ja_JP.UTF-8

# 3. システム既定のロケールを設定
$ sudo update-locale LANG=en_US.UTF-8

生成済みかは locale -a で再確認する。設定の永続先は /etc/default/locale

$ cat /etc/default/locale
LANG=en_US.UTF-8

対話メニューで選びたい場合は次のコマンドを使う。表示された一覧からロケールをスペースキーで選択して生成する。

$ sudo dpkg-reconfigure locales

update-locale の変更や /etc/default/locale新しいログインセッションから反映される。現在のシェルにすぐ効かせたいなら、一度ログアウト・再ログインするか、export LANG=en_US.UTF-8 で当座の値を入れる。

RHEL / Fedora 系での直し方

結論: RHEL 8 以降は glibc-langpack-<lang> パッケージでロケールを導入し、localectl set-locale で既定値を設定する。

RHEL / CentOS Stream / Fedora 系では、言語ごとの langpack を入れる方式。

# 英語ロケール一式
$ sudo dnf install -y glibc-langpack-en

# 日本語が必要なら
$ sudo dnf install -y glibc-langpack-ja

# システム既定を設定
$ sudo localectl set-locale LANG=en_US.UTF-8

localectl は systemd 系で共通のロケール管理コマンドで、設定は /etc/locale.conf に書き込まれる。現在値は localectl status で確認できる。

$ localectl status
   System Locale: LANG=en_US.UTF-8
       VC Keymap: us
      X11 Layout: us

Debian 系でも localectl(systemd)が使える環境なら localectl set-locale で既定値を設定できる。ただしロケールの生成自体は locale-gen が担うため、生成 → localectl の順は変わらない。

今すぐ黙らせたいときの応急処置

結論: export LC_ALL=C または export LANG=C.UTF-8 で、存在が保証されたロケールに切り替える。根本解決ではないが、警告を即座に止められる。

スクリプト実行やワンショットの作業で「今だけ警告を消したい」場合、確実に存在する C 系ロケールを指定する。

# 警告を完全に止める(英語メッセージ・ASCII 照合になる)
$ export LC_ALL=C

# UTF-8 を維持したい場合(多くの環境で利用可能)
$ export LANG=C.UTF-8
$ unset LC_ALL

C(= POSIX)と C.UTF-8 は glibc に組み込まれており、locale-gen 不要でほぼ確実に使える。C.UTF-8 を選べば、英語メッセージながら UTF-8 のバイト列は壊れずに扱える。

LC_ALLすべての LC_* を上書きする最優先変数。これを C に固定すると、日本語の文字種判定やソート順も C ロケールになる。恒久設定(.bashrc 等)に LC_ALL=C を書くと別の不具合を招くため、応急処置に留め、恒久対処はロケール生成で行う。

スクリプト単体で警告を抑えたいだけなら、先頭で次のように限定するのが安全。

#!/usr/bin/env bash
export LC_ALL=C.UTF-8

再発させないためのチェックリスト

結論: サーバに必要なロケールを生成済みにし、不要な SSH 転送を止め、LC_ALL の恒久固定を避ければ、ロケール警告はほぼ防げる。

対策 コマンド / 設定 効果
必要なロケールを生成 sudo locale-gen en_US.UTF-8 要求名の不在を解消
システム既定を明示 update-locale / localectl set-locale ログインごとの既定値を固定
不要な SSH 転送を止める SendEnv -LANG -LC_* 存在しないロケールの持ち込み防止
応急処置は局所に留める スクリプト内 export LC_ALL=C.UTF-8 恒久固定による副作用を回避

コピペ用:ロケール診断ワンライナー

# 要求中のロケールと利用可能な一覧を並べて確認
echo "--- requested ---"; locale 2>&1 | grep -E '^(LANG|LC_ALL|LC_CTYPE)='; \
  echo "--- available ---"; locale -a

次に読む