Linuxのコマンドをいくつか学んで慣れてきたころ、こう思うことはないでしょうか。
- 「
rmの削除は Windows のエクスプローラーでの削除と違って、特別な手順 (-rオプション) が必要なのは面倒。同じでいいから常に-rを付けよう」 - 「
sodo無しでもコマンドが成功するときと、sudoが無いと失敗することがある。失敗すると面倒だから常にsodoを付けよう」
私の知る限り、この発想に至る人は大勢います。ただ、より Linux に習熟した人たちは必ずこの発想から離れます。それは、他人の経験談や注意喚起によって、もしくは自らの痛みによって、この発想の恐ろしさを知るからです。
この記事では、上のような「とりあえず付けとけ」という発想が事故を生むような強力なオプション等を列挙することで、この発想に対して注意喚起することを目的にします。
-R (-r): 再帰的に処理するオプション
冒頭の例でも挙げたオプションです。ファイル (ディレクトリを含む) に対して何かをするコマンドの中には、ディレクトリ内のファイルに対しても一気に同じことをするオプションである -R (-r, --recursive) が指定できるものがあります。
対象のコマンド
| コマンド | コマンドの効果 |
-R オプションの効果 |
同じ効果のオプション |
|---|---|---|---|
rm |
ファイル削除 | ディレクトリ内のファイル (ディレクトリを含む) にも同じ処理(削除)をする |
-r, --recursive
|
chmod |
ファイルのパーミッションの変更 | ディレクトリ内のファイル (ディレクトリを含む) にも同じ処理(パーミッションの変更)をする | --recursive |
chown |
ファイルの所有者の変更 | ディレクトリ内のファイル (ディレクトリを含む) にも同じ処理(所有者の変更)をする | --recursive |
全てのコマンドで -R が再帰処理をするわけではありません。例えば vim での -R は readonly (書き込み不可) で開くオプションです。
このオプションの使い時
この再帰処理オプションは、共通して「ディレクトリ内のすべてのファイル(ディレクトリを含む)に同じ処理する」ことです。そのため、「ディレクトリ内全部を○○するぞ」 という気持ちの時だけつけるようにし、それ以外の時は付けないようにします。
乱用時の失敗事例
ディレクトリ内全部を... という気持ちが無いのに -R を付けることによる失敗事例を挙げます。
シンボリックリンク削除のつもりがディレクトリ内を...
この事例では、例えば次のような構成でファイルが存在します。
/home/user/redundant -> dir (シンボリックリンク)
/home/user/dir (ディレクトリ)
/home/user/dir/important1.txt (絶対に消してはいけないファイル)
/home/user/dir/important2.txt (絶対に消してはいけないファイル)
/home/user/dir/important3.txt (絶対に消してはいけないファイル)
ここで、シンボリックリンク redundant が不要になったので削除します。この時の正解は次の通りです。
$ rm /home/user/redundant
$
気を付けないといけないのは、末尾にスラッシュを付けないことです。なぜなら、/home/user/redundant はシンボリックリンクを指しますが、/home/user/redundant/ だとリンク先のディレクトリを指すからです。でも、これを間違えただけならまだ大丈夫。次のように、何も起こりません。
$ rm /home/user/redundant/
rm: cannot remove '/home/user/redundant/': Is a directory
$
$ ls -R /home/user
/home/user:
dir redundant
/home/user/dir:
important1.txt important2.txt important3.txt
$
ls の結果を見ての通り、全てのファイルが無事です。「リンク先 dir の中身全てを削除するぞ」という気持ち (-R) が無いことがちゃんと伝わっています。
一方で、手癖で -R を付けていると、次のように大惨事になります。
$ rm -R /home/user/redundant/
rm: cannot remove '/home/user/redundant/': Not a directory
$
$ ls -R /home/user
/home/user:
dir redundant
/home/user/dir:
$
一見、先ほどと同じようなエラーメッセージが出ているので同じ挙動かと思ったら、dir 内のすべてのファイルが消えてしまっています。どういうことかというと、-R の働きによって、「dir ディレクトリの中身に同じ処理(削除)をする」が実施され、その部分は完了してしまっているのです。そして最後に redundant を削除しようとしたときに、それができないというエラーが出て終了しているのです。
この失敗の直接的な要因はパスの末尾の / ですが、これはTabキーの自動補完やファイルパスをコピペした後の末尾のファイル名の削除などの操作の結果として、付けようとしなくても付いてしまうことがあります。目を皿にしてそれを見つけようとするよりも、「付ける操作をしないと付かない -R を付けない」ほうが対策として簡単で確実です。
sudo: root として実行 (他のユーザとして実行)
こちらはオプションではなくコマンドです。sudo の後ろにコマンドを付けることで、そのコマンドを root ユーザとして実行します。
sudo -u <user> <command> で root でない他のユーザとしてコマンドを実行することもできます。しかし、ここでは「手グセ」で使いがちな -u 無し (root としての実行) に絞って説明します。
このコマンドの使い時
root ユーザでないと実行できないコマンドを実行するために使用します。具体的な状況は多岐にわたりますが、例えば次のものが挙げられます。
- マシンを今すぐシャットダウンしたいとき (
sudo shutdown -h now) - root ユーザしか書き込み権限を持たない hosts ファイルを編集したいとき (
sudo vi /etc/hosts) - user01 が読み込み権限しか持たないディレクトリに user01 が所有するディレクトリを作りたいとき (ディレクトリ作成後に
sudo chown user01 <ディレクトリパス>)
いずれにしても、root ユーザでしか実行できないことをしたいときに sudo (-u オプション無し) を使用します。
root 権限でいくつも連続でコマンドを使いたい場合、そのすべてに sudo を付けることで手グセが付いてしまうとよくないので、root ユーザとしてシェルを起動できる sudo -i を使う方がよいでしょう。この場合、紹介する事例と類似の事故を防ぐため、root でないとできないことが終わったらすぐに exit で root としての操作を終了しましょう。
逆に「root が必要かわからないからとりあえず sudo 無しで実行してみよう」も、中途半端に処理が進んで面倒な状態になってしまうケースもあります。root 権限が関わる作業 (サーバ構築等) をする際は root の要不要をよく判断しましょう。
乱用時の失敗事例
root ユーザでないとできないことをしようと思っていないのに sudo を付けることによる失敗事例を挙げます。
再帰的にパーミッションを変えようとして ...
この事例では、自分が所有者であるディレクトリ /path/to/target/dir とその内容すべてのパーミッションを g+w (所有グループに書き込み権限を追加) で変更しようとしています。
自分が所有者 (再帰処理の対象となるすべてのファイルについて) なので、当然 sudo は不要で次のコマンドで実行できます。
$ cd /path/to/target/dir
$ chmod -R g+w .
ところがタイプミスによってルートディレクトリを指定してしまいました。
$ cd /path/to/target/dir
$ chmod -R g+w /
(/etc 等、このユーザでは権限不足なファイルに関して大量にエラーが出る)
この結果、(途中で止めなければ)所有者が自分であるすべてのファイルに対して g+w が実施されます。相当しんどいですが、chmod -R go-rwx ~/.ssh で SSH 認証系のパーミッションをもとに戻しさえすれば、後はどうにかなります。(完全に元に戻すには手間がかかるかもしれませんが)
一方で、「sudo が不要なのか考えるのが面倒だから」ととりあえずで sudo を付けていると次のようになります。
$ cd /path/to/target/dir
$ sudo chmod -R g+w /
(/etc 等、このユーザでは権限不足なファイルに関して大量にエラーが出る)
コマンド実行時の出力はさほど変わりませんが、状況は大きく異なります。まず、sudo が使えなくなっています。
$ sudo ls
sudo: /etc/sudo.conf is group writable
sudo: /etc/sudo.conf is group writable
sudo: /etc/sudo.conf, 0 行目 プラグイン "sudoers_policy" をロード中にエラーが発生しました
sudo: /usr/libexec/sudo/sudoers.so は所有者のみ書き込み可能で無ければいけません
sudo: 致命的エラー、プラグインをロードできません
また、SSH 接続もできなくなっています。(すでに接続しているセッションは維持されます)
> ssh <事故を起こしたホスト>
kex_exchange_identification: read: Connection reset
Connection reset by 192.168.56.10 port 22
>
他にも多くの機能が動作しなくなっていて、もはやホストを最初から作り直すほかなくなってしまいます。
この失敗の直接の原因はタイプミスによってルートディレクトリを対象としてしまったことですが、sudo が不要だった今回は sudo さえ使っていなければホストの作り直しは防げたかもしれません。sudo については次の点を意識するようにしましょう。
- よく考え、必要だと判断したときのみ
sudoを使用する -
sudoを使用する際は、実行する (Enterキーを押す) 前にコマンドを目視で確認する- 事前に実行するコマンドをテキストファイルに書いておいて事前に確認し、実行時はコマンドをコピペすることも効果的です(コピペのミスが無いかの目視確認は必要)
この事例を知ったうえで sudo の初回仕様時に表示される(環境による)下記のメッセージを読むと、#2, #3 が身に沁みます。1
あなたはシステム管理者から通常の講習を受けたはずです。
これは通常、以下の3点に要約されます:
#1) 他人のプライバシーを尊重すること。
#2) タイプする前に考えること。
#3) 大いなる力には大いなる責任が伴うこと。
-y: 確認を表示せずに進めるオプション
apt や dnf 等、コマンド実行後に「本当に実行してよいか」を確認してくるコマンドでは、-y オプションでこの確認をスキップすることができる場合があります。
少し慣れたころだと、いちいち y と返答しなくてもよくなる効率的なオプションとして -y を使うようになるかもしれませんが、これは危険です。多くの場合、確認が入るということは間違って実行したときの影響が大きいはずです。そのため、基本的には「本当に実行してよいか」の確認の前に表示される情報をもとにして、期待通りの動作が始まろうとしているかを確認するべきです。そのためには、これを阻害する -y は使わないのが基本となります。
異常を無視するオプション -f, --force があるコマンドもあります。基本的な考え方は -y と同じです。
全てのコマンドで -y が確認をスキップするわけではありません。例えば diff での -y は side-by-side で表示するオプションです。
このコマンドの使い時
このコマンドは Dockerfile などコマンドを自動で実行し完了すべき場面で使用します。そのような場面ではコマンド使用者が張り付いて状況を見ているわけではない(そもそも y を入力するインターフェースが無い) ので -y で確認をスキップしないと役目を果たせません。
yes: 「y」を出力し続けるコマンド
このコマンドは 「y」と改行を交互に出力し続けるので、後続のコマンドにパイプでつなぐことで「y」を入力し続けることができます。つまり、「本当に実行してよいか」を確認してくるコマンドに (-y が用意されていないコマンドでも) 自動で「y」を入力し、実質的に確認をスキップすることができます。
以降の説明は -y と同じなので省略します。
総括
まとめて言えることは、「○○機能を常に使えば(考えなくていいから)楽だ」という思い付きには常に「じゃあなぜ○○機能のオフがデフォルトになっているんだ」と考えるようにしましょうということです。そこには、『取り返しのつかないミス』を『何とかなるミス』や『ヒヤリハット(実害が生じなかったミス)』にするための優しさがあります。
-
この記事では #3 を「ミスったときに大きな影響を及ぼしうる」と捉えていますが、それよりも「あなたがそのコマンドを実行できるほどの知識・組織内での権限を持っていますか、その知識を使って熟慮しましたか」という側面が大きいと思います。 ↩