PR
PR

なぜ「sudo cd」は動かないのか?command not foundの原因と4つの代替テクニック

記事内に広告が含まれています。

UbuntuなどのLinux環境を使い始めて少し慣れてきた頃、誰もが一度は遭遇する「謎のエラー」があります。

$ cd /root
bash: cd: /root: Permission denied

「権限がないなら、管理者権限(sudo)をつけて移動すればいいじゃないか」と考えて実行してみると…

$ sudo cd /root
sudo: cd: command not found
sudo: "cd" is a shell built-in command, it cannot be run directly.
sudo: the -s option may be used to run a privileged shell.
sudo: the -D option may be used to run a command in a specific directory.

「さっきまで普通に使えていた「cd」コマンドが、なぜ見つからない(command not found)のか!?」

OSが壊れてしまったのかと不安になる方も多いですが、これはシステムの不具合ではなく、Linuxのシェルとプロセスの根本的な仕組みによる正常な挙動です。

この記事では、なぜ 「sudo cd」が使えないのかという技術的な理由を解説し、権限のないディレクトリを扱いたいときの 4つの代替テクニック をご紹介します。

なぜ「sudo cd」は動かないのか?2つの理由

「sudo cd」が失敗する背景には、「コマンドの正体」「Linuxのプロセス構造」 という2つの壁があります。

理由 1:cd は独立したプログラムではなく「シェル組み込みコマンド」だから

Linuxのコマンドには、大きく分けて以下の2種類が存在します。

  1. 外部コマンド(External Command): 「/usr/bin/ls」や「/usr/bin/cat」のように、独立した実行可能ファイル(バイナリ)として存在するコマンド。
  2. シェル組み込みコマンド(Shell Built-in): cd, pwd, exit, history, alias のように、現在動いているシェル(Bashなど)のプログラム本体に直接埋め込まれた内部機能。

「sudo」というコマンドの仕組みは、「指定された外部実行ファイルを環境変数(PATH)から検索し、root権限で実行する」 というものです。

しかし、「cd」という独立した実行ファイルはどこにも存在しないため、「sudo」は「そんな名前の実行ファイルは見つからないよ!」と「command not found」を返してしまうのです。

type コマンドでコマンドの正体を確かめてみよう

コマンドが外部ファイルなのかシェル組み込みなのかは、「type」コマンドで簡単に確認できます。

「cat」コマンドは「/usr/bin/cat」というファイルが存在する外部コマンドですが、「cd」コマンドは 「shell builtin」(シェル組み込み)と表示されます。

$ type cat
cat is /usr/bin/cat

$ type cd
cd is a shell builtin
補足:「ls is aliased to ...」と表示された場合

「type」を実行した際、以下のように「ls is aliased to ...」と表示されることがあります。

これはエイリアスが設定されていることを表します。

$ type ls
ls is aliased to `ls --color=auto'

エイリアスではなくバイナリ本体の位置を調べるには「-P」オプションを使います。

$ type -P ls
/usr/bin/ls

理由 2:プロセスの壁(子プロセスは親プロセスの現在地を変えられない)

「では、もし仮に「/usr/bin/cd」という外部コマンドを作成したら「sudo cd できるのでは?」と思うかもしれませんが、それを作っても意図通りには動きません

Linuxでは、プログラムを実行するたびに新しい 「子プロセス(別の部屋)」がフォーク(生成)されます。

カレントディレクトリ(現在いるディレクトリ)は、それぞれのプロセスが個別に持っている情報です。

[現在のシェル(親プロセス)]
      │
      ▼ (sudo cd を実行)
  [sudoプロセス(子プロセス)]  <-- root権限で別プロセスが立ち上がる
        │
        ▼
      ディレクトリを /root に移動した!
        │
      (処理終了とともに子プロセスは消滅)
        ▼
[現在のシェル(親プロセス)]  <-- 現在地は最初の場所から変わっていない!

仮に子プロセスがroot権限で「/root」に移動できたとしても、コマンドが終わった瞬間に子プロセスは消滅し、親プロセス(私たちが操作している画面)に戻ってきます。

Linuxのセキュリティ設計上、子プロセスが親プロセスのカレントディレクトリ(作業ディレクトリ)を勝手に書き換えることはできないため、結果的に「移動できなかった」ことになります。

親プロセスのカレントディレクトリを変更できるのは、自分自身のプロセス空間を直接操作できる「シェル組み込みコマンド」の「cd」だけなのです。

【逆引き】シチュエーション別・4つの解決アプローチ一覧

root権限が必要なディレクトリ(/root や /etc/letsencrypt/liveなど)を扱いたいときは、以下の4つの代替アプローチから状況に合わせて選びます。

アプローチ 推奨度 コマンド例 最適なシチュエーション
直接操作 ★★★★★(最推奨) sudo ls -la /root
sudo vi /root/.bashrc
ファイルを閲覧・編集したい日常の作業全般
単発実行 ★★★★☆ sudo sh -c "cd /root && ls -la" カレントディレクトリ依存のスクリプトやワンライナーを特定場所で動かしたい時
rootシェル ★★★☆☆(要慎重) sudo -i
(作業後は必ず exit)
何十回も連続でroot作業を行うメンテナンス時
ユーザー指定 ★★★★☆ sudo -u nginx ls -la /usr/share/nginx/wordpress Webサーバー等のサービス権限での動作検証時

【最推奨】ディレクトリ移動せず外から直接操作する

そもそもディレクトリを移動してやりたいことは何でしょうか?

「ディレクトリの中身を見たい」「ファイルの中身を確認したい」「ファイルを編集したい」といった目的であれば、わざわざ「cd」で移動する必要はありません。

元の場所にいたまま、「sudo」を付けて直接コマンドを実行するだけで目的を達成できます。

ディレクトリの中身を見たい場合

$ sudo ls -la /root

ファイルの中身を確認したい場合

$ sudo cat /root/.bashrc
$ sudo less /var/log/auth.log

ファイルを編集したい場合

$ sudo vi /etc/hosts

単発作業なら「sudo sh -c」を使う

「どうしてもカレントディレクトリがその場所でないと動かないスクリプトやツールを実行したい」という場合は、「sudo」で新しいシェルを起動し、その中で「cd」を実行させます。

$ sudo sh -c "cd /root && pwd && ls -la"
  • 仕組み: 「sudo」が管理者権限で sh(シェル)を立ち上げ、そのシェルの内部で組み込みコマンド cd を実行しています。
  • メリット: sh -c の処理が終わると自動的にrootシェルが終了し、元の一般ユーザーシェルに戻ってきます。「rootのまま放置してしまう危険」がありません。

sudo bash -c でもいいの?

「sudo bash -c」を使用しても同様に動作します。

一般的なスクリプトやワンライナーでは移植性の高い「sh」が多く使われますが、配列や [[ ... ]] 構文などの Bash 固有の機能を使用したい場合は、明示的に「sudo bash -c」を指定すると確実です。

連続作業なら一時的に root シェルに入る(要注意!)

設定ファイルの修正やログ調査など、何度も続けてroot権限での操作が必要な場合は、「sudo -i」で一時的にroot権限のシェルに切り替えます。

実行すると、プロンプトの末尾が一般ユーザーの「$」から、rootユーザーを表す「#」に変化し、自動的にカレントディレクトリも「/root」に移動します。

このシェル内であれば、管理者権限であらゆるディレクトリへ自由に「cd」で移動できます。

$ sudo -i
# pwd
/root

現在のカレントディレクトリを維持したままroot権限シェルを立ち上げたい場合は、「sudo -s」を使用します。

$ pwd
/var
$ sudo -s
# pwd
/var

作業が終わったら必ず「exit」すること!

rootシェルの状態のまま作業を続けると、操作ミス一つでシステム全体に致命的な影響を与えるリスクがあります。

目的の作業が終わったら、「exit」を入力して一般ユーザーに戻ってください。

# exit
logout
$

特定ユーザーの権限で操作する(sudo -u)

rootではなく別ユーザの権限で作業を行いたい場面があります。

その場合は「-u」オプションを使用します。

例として「tamo」ユーザーの権限で「/home/tamo」(ホームディレクトリ)の中身を確認してみます。

$ sudo -u tamo ls -la /home/tamo
total 24
drwxr-x--- 2 tamo tamo 4096 Jun 11 14:08 .
drwxr-xr-x 4 root root 4096 Oct 18  2024 ..
-rw------- 1 tamo tamo  454 Sep  9 09:04 .bash_history
-rw-r--r-- 1 tamo tamo  220 Oct 18  2024 .bash_logout
-rw-r--r-- 1 tamo tamo 3771 Oct 18  2024 .bashrc
-rw-r--r-- 1 tamo tamo  807 Oct 18  2024 .profile

さらに、「-s」オプションを一緒に使用すると、そのユーザーの権限でシェルを起動することも出来ます。

$ sudo -u tamo -s
$ id 
uid=1001(tamo) gid=1001(tamo) groups=1001(tamo),100(users)

まとめ

  • sudo cd ができない理由: cd はファイルではなく「シェル組み込みコマンド」であり、子プロセスが親プロセスの現在地を変えることはできないため。
  • 普段の作業は外から直接操作する: sudo ls /root や sudo nano /root/... を使うのが最も安全なプロの作法。
  • 特定ディレクトリでの単発実行は sudo sh -c: ワンライナーで安全に完結させる。
  • 連続作業で sudo -i を使ったら必ず exit: rootシェルに居座らず、終わったら即座に抜ける。

コメント

タイトルとURLをコピーしました