# コマンドを変数に登録 CLAUDE ="/home/...   /bin/claude" $ " $CLAUDE " login systemd でスクリプトが
動かない?
(シェルとPATH)

  • systemdからスクリプトを実行すると、ログインシェルが読み込むPATH設定が反映されないため、nvmなどでインストールしたコマンドが見つからなくなります。
  • 対策として、スクリプト内でコマンドを絶対パスで呼び出し、変数に入れて管理するのが確実です。
  • grepやsedなどシステム標準のコマンドは通常のPATHで見つかるため、基本的には絶対パス指定は不要です。
CLAUDE="/home/username/.nvm/versions/node/v24.14.0/bin/claude"
"$CLAUDE" loginCode language: JavaScript (javascript)

関連記事

1. sytemdだとPATHが違う?

ふだんターミナルで使っているスクリプトを systemctl から呼び出したら、エラーになっていました。

systemd でスクリプトが動かない理由 ログインシェル ~/.bashrc を読み込む → nvmのPATHが有効 vs systemd サービス 設定ファイルを読まない → nvmのPATHが無効 nvm install claude CLI ~/.nvm/versions/ node/vX/bin/ systemd PATHに未登録 command not found: claude

どうも、ログインシェルでないためにPATH が違うため、nvmなどでインストールしたローカルのCLIコマンドが見つからなくなっていたみたいです。

PATH とは、シェルがコマンドを探すディレクトリの一覧です。
ログインシェルでは ~/.profile~/.bashrc/etc/profile などで読み込みますが、systemd のサービスはこれらを読み込みません。
そのため、PATH に含まれないコマンドが見つからなくなるのです1

たとえば、nvm(バージョン管理ツール)でインストールした claudecodex などは ~/.nvm/versions/node/vX.X.X/bin/ 以下に入ります2
このディレクトリは systemd の標準 PATH に含まれないため、特に問題が起きやすいです。

1.1. コマンドを変数で管理する

systemdのサービス登録で、ENVIRONMENTにPATHを登録する方法もありますが、確実なのはスクリプト内でコマンドを絶対パスで呼び出すことです。

たとえば、スクリプトの冒頭でコマンドのパスを変数に登録します。

CLAUDE="/home/username/.nvm/versions/node/v24.14.0/bin/claude"Code language: JavaScript (javascript)

usernameやバージョンは、それぞれの環境に合わせて変えてください。
実行時は引用符を付けて$変数名 で呼び出します。

"$CLAUDE" loginCode language: JavaScript (javascript)

これは、パスに空白が含まれる場合でも安全に動作するための作法です3
今回は、空白文字がないので、どちらでも効果は変わりませんが、わかりづらいエラーを避けるための習慣ですね。

ちょっと面倒にはなりますが、この方法の利点は、

  • PATH が異なる環境でも確実に動く
  • コマンドのパスを一箇所で管理できる
  • nvm のバージョンを上げたときは変数の値を書き換えるだけで済む4

1.2. grep や sed はどうするか

このような絶対パスでのコマンド呼び出しは、自分のインストールしたコマンドに対して行えば十分です。

たとえば、grepsedawk などまで絶対パスで呼び出すのは大変で、そこまでする必要はありません。
通常 /usr/bin/ に入っており、systemd の標準 PATH で見つかるからです5

あとは、スクリプト冒頭で PATH を明示しておく方法もあります。

PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/binCode language: JavaScript (javascript)

ただ、~/.nvm/... のようにユーザーディレクトリ以下に入るコマンドはこの方法では解決しないため、やはり変数で管理する必要は残ります。

2. 動作確認の方法(systemd-run)

systemd の実行環境に近い状態でスクリプトをテストするには systemd-run を使います6

解決策と動作確認 解決策 1 絶対パスを変数で管理 CLAUDE=”/home/user/.nvm/…/claude” “$CLAUDE” login 2 シンボリックリンクで固定 ~/.local/bin/claude → nvm配下 3 systemd Environment に追加 Environment=”PATH=/usr/bin:…” 動作確認 環境変数の確認 systemd-run –user –pty env テスト実行 systemd-run –user –pty script.sh 起動前チェック [ -x “$CLAUDE” ] || \ { echo “not found: $CLAUDE”; exit 1; }
systemd-run --user --pty /path/to/script.sh

また、環境変数の一覧は次のコマンドで確認できます。

systemctl show-environment
systemd-run --user --pty env

2.1. 起動前の存在確認

スクリプトの冒頭でコマンドの存在を確認しておくと、パスが変わった場合にすぐ検出できます。

[ -x "$CLAUDE" ] || { echo "claude not found"; exit 1; }Code language: PHP (php)

-x は、指定したファイルが存在して実行可能かどうかを確認するテストです7

  1. systemd のサービスはクリーンな実行環境で起動し、ログインシェルが読む設定ファイルを一切参照しません。環境変数を渡すには unit ファイルの Environment= または EnvironmentFile= を使います。 – Setting Environment Variables for systemd Services
  2. nvm は Node.js のバージョンごとに $NVM_DIR/versions/node/vX.X.X/ 以下にファイルを格納します。グローバルインストールした npm パッケージのバイナリも同じディレクトリの bin/ に置かれます。 – nvm-sh/nvm
  3. シェルはダブルクォートで囲まれた変数を単一のトークンとして扱います。引用符なしでパスに空白が含まれると、シェルのワード分割によってコマンドと引数が分離され、意図しない動作を引き起こします。 – Bash Reference Manual – Quoting
  4. nvm でバージョンを切り替えると、使用するバイナリのディレクトリパスが変わります。スクリプト上部の変数定義を一行修正するだけで対応できます。現在のバージョンは nvm current で確認できます。 – Installing Multiple Versions of Node.js Using nvm
  5. systemd がサービスに渡すデフォルトの PATH/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin です。実際の値は systemd-run env をジャーナルで確認できます。 – Arch Linux Forums – Best way to set the executable path
  6. systemd-run はコマンドをトランジェントサービスとして実行するツールです。--pty は疑似端末(pseudo TTY)を通じて標準入出力をターミナルに接続するオプションで、インタラクティブな出力が必要なスクリプトのテストに適しています。 – Ubuntu Manpage: systemd-run
  7. -x フラグは POSIX で定義されており、[ ]test コマンドのどちらでも使えます。ファイルが存在し、かつ実行パーミッション(execute bit)が立っているときに真を返します。 – test – Open Group POSIX 仕様