シェルスクリプトでコマンド事故を防ぐ

掲載日
更新日

はじめに

今回はshell scriptの話です。

本業でサーバー構築を多少やってるのですが、弊社は意外と皆さんコマンドのマニュアルも読まなければオプションも滅茶苦茶な使い方をされているので、啓蒙として。
(自分も所詮元文系の独学なので、色々と「偉そうに書いてる割にこんなことも分かんないのかよ。」みたいな抜け漏れがあると思います。ご教授いただけると大変喜びます。)

この記事のターゲット

  • Linux始めたてでコマンドわからない人

man(manual)コマンドで使い方を知ろう

各コマンドの頭に man を付けることで、そのコマンドのマニュアルが表示されます。(以下のコマンドで、sortコマンドのオプションが見れます。)
言語設定によっては英語なので読むのが大変ですが、少なくとも標準的なコマンドであれば小難しい英語が出てきたりはしないと思います。

# man sort

SORT(1)                          User Commands                         SORT(1)

NAME
       sort - sort lines of text files

SYNOPSIS
       sort [OPTION]... [FILE]...
       sort [OPTION]... --files0-from=F

DESCRIPTION
       Write sorted concatenation of all FILE(s) to standard output.

       With no FILE, or when FILE is -, read standard input.

       Mandatory  arguments  to  long  options are mandatory for short options
       too.  Ordering options:

       -b, --ignore-leading-blanks
              ignore leading blanks
...(以下略)...

コマンドを使うときの指定の仕方や、オプションとその説明を見ることができます。
上記のsortのオプションを読むと、実は面白いオプションがあることも分かります。それが以下。

...(略)...
       -u, --unique
              with -c, check for strict ordering; without -c, output only  the
              first of an equal run
...(略)...

そう、実はsortコマンドでuniqコマンドみたいなことが出来るのです。
あるファイルをソートして、重複を消して出力したいとなった場合、以下のコマンドを入力する方は結構多いと思いますが、

sort hoge.txt | uniq

実は、以下でも同じようなことができるんですね。

sort hoge.txt -u

とはいえ、こういう罠もあるし、入力の文字数が劇的に減ってワンライナーの可読性が上がるわけでもないので、用途に合わせて適切に使い分けてね!ということにはなるのですが、「マニュアルを読むと意外なオプションを知ることができて楽しいよ!なので是非暇なときでいいから、man [コマンド名] しよう!」という例として。

詳細ログを出そう

多くのコマンドで実装されているオプションの一つに、「-v(verbose)」というものがあります。
(使おうとしているコマンドにそのオプションが存在するか、あるいは「-v」以外の形式で存在するかは、前述の「man」コマンドで確認できます。)

このオプションを指定すると「詳細なログを出力」してくれます。(※version情報の出力に「-v」が割り当たっていることも多いので、くどいですがmanコマンドで確認しましょう。)

例えばみんな大好き「rm」コマンドも「-v」を付けることで削除したファイル名を表示してくれます。
ここで再びみんな大好き、「rm -rf」をうっかり変なパスを指定して実行してしまったとします。
この時、vオプションがついていれば削除したファイルをすべてログに出力してくれます。

# rm -rfv testdir
removed 'testdir/b.txt'
removed 'testdir/a.txt'
removed 'testdir/c.txt'
removed directory 'testdir' 

消し飛ばした場所によってはログが残ってても無理ですが、削除したファイル名が分かっていれば、バックアップ等からなんとかデータを探し出したり、リカバリが出来るケースもあります
ユーザーがアップロードしたファイルを格納しているディレクトリを消し飛ばした場合に、当日0時までバックアップがあれば、そこまでは復旧して、差分のファイル名も出せるので、お客さんに頭を下げてこの一覧に載っているファイルを再度アップしてください、ということも出来ますよね。
(「何が消えたかはわからないけど、本日アップしたデータもう一回アップして」と言われるよりはユーザー的には助かりますよね。)

また、本当にあった怖い話として「chown -R apache:apache /」をした人がいます。
すべてのオーナーがapacheになったので当然いろんなものが動かなくなり、OSインストールからやり直しとなりました。
ログも出ていないのでどのディレクトリ・ファイルにどのユーザーを割り当てていたかはもはや分かりません。
ログが出ていれば、以下のように「誰から誰に変わったか」を教えてくれるので、逆の操作を行って復旧もできたかもしれません。(さすがに弊社のようにルートから行かれると厳しい気がしますが、これが特定のディレクトリだけとかなら復旧も出来そうな気がしてきますよね。)

# chown -Rv apache:apache testdir
changed ownership of 'testdir/a.txt' from root:root to apache:apache
changed ownership of 'testdir' from root:root to apache:apache

ちなみに、ネットでは「rm -rf」が禁忌として伝わっていますが、上記の怖い話からも分かる通り、本当の禁忌は「recursive」オプションだと思います。

その名の通り再帰的に処理を行うので、Linux初心者はもちろん、時に熟練者でもうっかり入力中にエンターキーに指が掠って変なパスでコマンドを実行したりします。(本番環境でアドリブでコマンドを作るなって言われたらおっしゃる通りです。)

使用するなら最低でもログを出すことは徹底した方が良いと思います。
他にも、以下の手順を踏んだらもっと安全かなと思います。
(※これはもっと安全で楽な方法がある気がするので、誰か知ってる人いたら教えてください。)

  1. findコマンドなどで削除したいディレクトリ・ファイル一覧を出力
  2. 出力された一覧が消していいものか確認する
  3. xargsコマンドを使ってrm -f(d) する等。(xargsも怖いので、自分はいったんテキストエディタで矩形選択でコマンド作ってコピペしてます。)

 

とにかく再帰オプションを使わなければ消えるのはそのコマンドで指定したディレクトリ・ファイルだけで済みます。(ディレクトリ配下に何かしらデータがあればfコマンドがあっても消されません。)

予行演習をしよう

これは詳細ログを出力するオプションより対応しているコマンドがそんなに多くないと思いますが、例えば同期をとるためのコマンドとして有名な「rsync」は予行演習を行えます

「man rsync」で是非確認いただくと以下のような説明文が出てきます。

--dry-run, -n            perform a trial run with no changes made

説明文にある通り、「変更なしで試運転」を行います。

例えば testdir1 から testdir2 に、testdir1に入っているデータを同期したいとします。
ここでコマンドの構文等を確認せず、手癖で以下のようなコマンドを実行します。

rsync -av testdir1 testdir2

すると、testdir2の下にtestdir1ディレクトリが出てきます。当たり前ですね。
ではこの時予行演習をしていたらどうなったでしょうか。

#rsync -av --dry-run testdir1 testdir2
sending incremental file list
testdir1/
testdir1/a.txt

予行演習のログが表示されるので、testdir1ディレクトリがコピーされてしまったことが分かりました。
ここで操作者はそうだ、ディレクトリ配下のデータを同期したいときは「/」を付けないといけないと気が付いて修正することができます。便利ですね。

# rsync -av --dry-run testdir1/ testdir2
sending incremental file list
./
a.txt

なお、この例ならファイル名の重複でもない限りリカバリが効くと思いますが、問題はrsyncの「--delete」オプション使用時です。

このオプションを使うと、左側にないデータが右側にある場合、右側のデータを削除します。
例えば以下のようなディレクトリがあったとします。

# ll testdir1
total 0
-rw-r--r-- 1 apache apache 0 Jul 12 22:50 a.txt
# ll testdir2/
total 0
-rw-r--r-- 1 apache apache 0 Jul 12 22:50 a.txt
-rw-r--r-- 1 root   root   0 Jul 12 23:13 b.txt

ここでdeleteオプション付きでrsyncを実行してみると、以下のようになります。

# rsync -av --delete  testdir1/ testdir2
sending incremental file list
deleting b.txt

sent 73 bytes  received 21 bytes  188.00 bytes/sec
total size is 0  speedup is 0.00
# ll testdir1/
total 0
-rw-r--r-- 1 apache apache 0 Jul 12 22:50 a.txt
# ll testdir2/
total 0
-rw-r--r-- 1 apache apache 0 Jul 12 22:50 a.txt

もちろんこれが狙い通りの動作ならOKです。(例えばWebサイト等で、ユーザーがアップロードしたファイルをバックアップしているが、ユーザーがサイト側で削除処理をしたデータは規約上バックアップも削除しないといけないとかなら、このオプションを入れておけばOK。)

ただ、手癖で書いていたり、どこぞから脳死コピペしてきた場合に大事故を起こすことは言うまでもありません。
これも「--dry-run」をしていればログに「deleting b.txt」と出てくるので気が付けますね。

構文チェックしよう

apache等を使用している場合、httpd.confなどを編集することになります。
この設定内容が間違っていると、当然Webサービスは正常に動かなくなってしまいます。
とはいえ変更後即座に再起動するならいいんです。止まったら慌てて追加した設定消す、もしくはバックアップ復旧して再度再起動すればそんなに停止時間は長くないでしょう。

本当にあった怖い話なんですが、間違った設定で保存しておいて再起動もせずにログアウトし、翌日早朝にログローテーションによってApache再起動するも構文間違ってるのでサービス停止、早朝なので全員爆睡していてN時間停止、弊社より始業が速い顧客が気が付き…ということをする人は本当にいます。(弊社だけ?ならいいなぁ…)

翌日から設定反映したいケースもあるので、再起動しないこと自体はいいんです。(httpd.confで時間指定で設定反映もできた気もしますがきっと気のせいでしょう。)
ですがhttpdコマンドには素晴らしいオプションがあるんです。「-t」って言います。

-t     Run syntax tests for configuration files only. The program immediately exits after these syntax parsing tests with either a return code  of
       0  (Syntax  OK) or return code not equal to 0 (Syntax Error). If -D DUMP_VHOSTS is also set, details of the virtual host configuration will
       be printed. If -D DUMP_MODULES  is set, all loaded modules will be printed.

なんだか英文が並んでいて大変ですが、「Run syntax tests for configuration files only.」くらいは雰囲気で分かります。設定ファイルのシンタックステストを実行するようです。
「only」とあるのが気になるところではありますが、少なくともこのオプションを付けることで、設定ファイルの構文チェックをしてくれます。

なんなら構文以外もチェックしてくれます。例えばDocumentRootに設定しているディレクトリが間違っているとこんな風にWarningを出してくれます。

[root@awatana tmp]# httpd -t
AH00112: Warning: DocumentRoot [/konnadirectorynaiyo/] does not exist
Syntax OK

しよう!構文チェック

おわりに

偉そうにつらつらと書いてきましたが、自分はインフラエンジニアではないし、shell script独学の素人ですから色々抜け漏れや、もっと簡単に対策できることをややこしく考えすぎてたりすると思います。

manコマンドも自分も都度確認はしてません。困ったときに何かいいオプションないかな~くらいの頻度で覗くくらいでも十分だと思います。
今回伝えたいことは「ログを出そう」「予行演習できるコマンドなら予行演習しよう」「構文チェックしよう」、そして「manコマンドを使えばこれらのことが行えるオプションがあるかチェックできるよ!」ということです。
ほんの少しオプションを付けるだけでオペミスを減らせますし、オペミスしてからのリカバリが出来ることもあります。

※ 本番機でいきなりコマンド実行しないで検証環境機でコマンド作って、手順書作ってから実行しろと言われたら全くその通りだと思います。
(なお現実では検証機がなかったり、なぜか検証環境と本番環境で何らかの状態が違っていて本番機作業中にアドリブ作業が発生したりします。なんで?)

記事の作成者のA.W.のアイコン

この記事を書いた人

A.W.
茨城県在住Webエンジニアです。 PHPなどを業務で使用しています。 趣味ではGoやNuxt、Flutterをやってます。

Comment