env コマンド入門 - 環境変数を一時設定してコマンドを実行する

env コマンド入門 - 環境変数を一時設定してコマンドを実行する

env コマンドとは?

結論: env は環境変数を一時的に変更してコマンドを実行する coreutils のツール。env VAR=value command の形で、現在のシェルを汚さずに特定のコマンドだけ別の環境で走らせられる。

env には大きく 2 つの用途がある。

  • 環境変数を一時設定して実行するenv LANG=C date のように、その 1 回だけ変数を変える)
  • 引数なしで現在の環境変数を一覧するprintenv と同等)

加えて、スクリプトの shebang 行(#!/usr/bin/env python3)でインタプリタを PATH から探させる用途でも広く使われる。

この記事で扱う環境

  • GNU coreutils の env(Ubuntu / Debian / RHEL 系など一般的な Linux ディストリビューション)
  • 一部のオプション(-C / -S)は coreutils のバージョンに依存する。後述

なぜ env VAR=value を使うのか?

結論: 設定を恒久化せず現在のシェルも汚さず、その 1 コマンドにだけ環境変数を効かせたいから。export は以降ずっと残るが、env は一過性で済む。

環境変数を変えてコマンドを動かす方法は複数ある。違いを押さえておく。

# (1) シェル組み込みの一時代入(env なし)
$ LANG=C date

# (2) env 経由の一時代入
$ env LANG=C date

# (3) export(以降のコマンドすべてに影響、恒久的)
$ export LANG=C
$ date

(1) と (2) はどちらも「その 1 回だけ」変数を変える点で同じ結果になる。一方 (3) の export は、それ以降に実行するすべてのコマンドへ影響が残る。「いまの 1 回だけ言語を変えたい」「設定を後に残したくない」場面では (1) か (2) を選ぶ。

(1) と (2) はどう違うのか

  • LANG=C date はシェルの構文(前置代入)。シェルが解釈する
  • env LANG=C dateenv という独立した実行ファイルを起動し、その子プロセスで環境を組み立ててから dateexec する

通常の対話用途では結果は同じ。env が必須になるのは、シェルを介さない場面(shebang 行、exec 系 API への引数、空環境からの起動など)。

環境変数を一時設定して実行する

結論: env NAME=value command args... と書く。複数の変数はスペース区切りで並べられる。

# タイムゾーンを一時的に UTC にして日時表示
$ env TZ=UTC date

# 言語を C ロケールにして英語メッセージを得る(ログ調査で定番)
$ env LANG=C ls /nonexistent
ls: cannot access '/nonexistent': No such file or directory

複数の変数を同時に設定する場合は続けて並べる。

$ env LANG=C LC_ALL=C TZ=UTC ./myscript.sh

ロケール起因のエラー切り分け

日本語環境ではエラーメッセージが翻訳されて検索しづらいことがある。env LANG=C コマンド で英語メッセージに固定すると、原文での検索や issue 報告がしやすくなる。

空の環境で実行する(env -i)

結論: env -i継承した環境変数をすべて消した状態でコマンドを起動する。「自分の環境でだけ動く / 落ちる」問題の切り分けに効く。

# 何が残るかを確認(ほぼ空になる)
$ env -i env

-i--ignore-environment)を付けると、PATH すら継承されない。そのため -i 環境ではコマンドを絶対パスで指定するか、必要な変数を明示的に渡す必要がある。

# PATH が無いので which 等は失敗しうる。必要な変数だけ渡す
$ env -i PATH=/usr/bin:/bin LANG=C ./myscript.sh

env -i 配下では HOME / PATH / LANG などが未定義になる。スクリプトがこれらに依存していると予期せぬ挙動になる。必要な変数だけを明示的に渡すのが安全な型。

特定の変数だけ外して実行する(env -u)

結論: env -u NAME は、その環境変数だけを取り除いてコマンドを実行する。-i のような全消しではなく、一点だけ無効化したいときに使う。

# プロキシ設定を一時的に無効化して接続を試す
$ env -u http_proxy -u https_proxy curl https://example.com

-u--unset)は変数ごとに繰り返し指定できる。「この環境変数が悪さしているのでは?」という仮説を、設定ファイルをいじらずに即検証できる。

ディレクトリを変えて実行する(env -C)

結論: env -C DIR command は、指定ディレクトリへ移動してからコマンドを実行する。cd DIR && command をサブシェルなしで 1 行にまとめられる。

$ env -C /var/log tail -n 20 syslog

-C--chdir)は coreutils 8.28 以降で利用可能。古い環境では使えない。env --version でバージョンを確認できる(後述)。

shebang で使う /usr/bin/env

結論: スクリプト先頭の #!/usr/bin/env python3 は、インタプリタを PATH から探して起動する。インタプリタの絶対パスをハードコードせずに済み、可搬性が高い。

#!/usr/bin/env python3
print("hello")

#!/usr/bin/python3 と直接書くと、Python が /usr/local/bin や仮想環境(venv)など別の場所にある環境で動かない。#!/usr/bin/env python3 なら PATH を探索するため、その人の環境で最初に見つかる python3 が使われる。

#!/usr/bin/env -S python3 -u

shebang 行でインタプリタに引数を渡したい場合は -S--split-string)を使う。shebang は本来 1 引数しか渡せないが、-S が文字列を分割して複数引数として扱う。

-Scoreutils 8.30 以降。macOS(BSD env)でも対応状況が異なるため、配布スクリプトで使う場合は対象環境を確認する。

printenv / set との違い

結論: 引数なしの env は環境変数の一覧だけを表示する。シェル変数を含む全変数は set、特定の 1 変数は printenv NAME が向く。

やりたいこと コマンド
環境変数を一覧 env または printenv
特定の環境変数だけ表示 printenv PATHenv 単体では絞り込めない)
シェル変数も含めて全部 set(bash 組み込み)
一時設定して実行 env VAR=val cmd

env PATH のように引数を 1 つ渡しても変数値は表示されない(env はそれをコマンド名と解釈し「No such file or directory」になる)。値だけ見たいときは printenv PATHecho "$PATH" を使う。

トラブルシューティング

結論: env 特有のつまずきは「-i 後に PATH が無い」「-C / -S が古い環境で使えない」の 2 つが大半。バージョン確認で多くが切り分く。

env: '...': No such file or directory と出る

env= を含まない最初の引数をコマンド名として扱う。env LANG C date= を書き忘れ)や env PATH(値を見ようとした)でこのエラーになる。NAME=value の形になっているか確認する。

env -i の後でコマンドが見つからない

-iPATH も消す。env -i PATH=/usr/bin:/bin コマンド のように PATH を明示するか、コマンドを絶対パスで指定する。

# 利用中の env のバージョンを確認(-C / -S の可否判断に使う)
$ env --version | head -1
env (GNU coreutils) 9.4

次に読む