~/blog/implementing-a-prompt-to-warn-before-executing-rm-rf-command-in-bash
Published on

Bashでrm -rf *コマンドが実行される前に警告するプロンプトを実装する

4933文字9分で読めます–––
閲覧数
Authors
  • avatar
    Name
    Shou Arisaka nyapp.buzz/shou
    short bio
    Z世代の情報技術者。Next.jsで自作SNSを個人開発中。

Linuxパソコン・サーバーのBashプログラミング言語(スクリプティング言語)上のコマンドラインにおいて、"rm -rf *"`コマンドが実行される前に警告するプロンプトを実装する方法について紹介します。

Linuxコマンドrm -rfは最も危険なコマンドのひとつとして名高いですね。近年ではサーバー管理者が意図せずこのコマンドを実行してしまい全データを消してしまったという話も聞いたことがあります。

今回はそんなrm -rfコマンドの危険性を少しでも下げることができたらという思いつきから書いたコードを紹介します。

使用例

https://yuis.xsrv.jp/images/ss/ShareX_ScreenShot_2675b7f6-aeb0-4f5b-a77e-31850e7064d8.gif
$ tmpdird # 新規ディレクトリへ移動

$ ls
total 0
166351711236005864 drwxrwxrwx 1 yuis yuis 4096 Jul 10 21:59 ..
 23080948093026057 drwxrwxrwx 1 yuis yuis 4096 Jul 10 21:59 .

$ genfiles 3 # ファイルを生成

$ ls
total 0
166351711236005864 drwxrwxrwx 1 yuis yuis 4096 Jul 10 21:59 ..
  8444249304069546 -rwxrwxrwx 1 yuis yuis    2 Jul 10 21:59 3.txt
 23080948093026057 drwxrwxrwx 1 yuis yuis 4096 Jul 10 21:59 .
 51509920740553578 -rwxrwxrwx 1 yuis yuis    2 Jul 10 21:59 2.txt
 36310271996515302 -rwxrwxrwx 1 yuis yuis    2 Jul 10 21:59 1.txt

$ rm -rf * # 全ファイルを削除 > 確認プロンプト > n
W: It seems You are attempting to run kind of dangerous command. Continue? [y/n]
n

$ ls # ファイルが消されていないことを確認
total 0
166351711236005864 drwxrwxrwx 1 yuis yuis 4096 Jul 10 21:59 ..
  8444249304069546 -rwxrwxrwx 1 yuis yuis    2 Jul 10 21:59 3.txt
 23080948093026057 drwxrwxrwx 1 yuis yuis 4096 Jul 10 21:59 .
 51509920740553578 -rwxrwxrwx 1 yuis yuis    2 Jul 10 21:59 2.txt
 36310271996515302 -rwxrwxrwx 1 yuis yuis    2 Jul 10 21:59 1.txt

$ rm -rf * # 全ファイルを削除 > 確認プロンプト > y
W: It seems You are attempting to run kind of dangerous command. Continue? [y/n]
y

$ ls # ファイルが消されてることを確認
total 0
166351711236005864 drwxrwxrwx 1 yuis yuis 4096 Jul 10 21:59 ..
 23080948093026057 drwxrwxrwx 1 yuis yuis 4096 Jul 10 21:59 .

上記のような具合に、rm -rf *を実行しようとすると、実行される前に警告を行うプロンプトをします。yで実行をし、nで実行をキャンセルします。

コード


ok(){

  : <<< '
  yes or no prompt
  e.g. printf "The file alredy exist here. Override it? " ; ok && echo y || echo n
  '

  read -n 1 -r ; [[ $REPLY =~ ^[Yy]$ ]] && { echo ; return 0 ; } || { echo ; return 1 ; }

}

red=
\e[1;31m'
grn=
\e[1;32m'
yel=
\e[1;33m'
blu=
\e[1;34m'
mag=
\e[1;35m'
cyn=
\e[1;36m'
end=
\e[0m'

warn(){
  printf "${yel}W: ${*}\n${end}" >&2
}

##

shopt -s extdebug

preexec_invoke_exec () {
    [ -n "$COMP_LINE" ] && return  # do nothing if completing
    [ "$BASH_COMMAND" = "$PROMPT_COMMAND" ] && return # don't cause a preexec for $PROMPT_COMMAND
    local this_command=`HISTTIMEFORMAT= history 1 | sed -e "s/^[ ]*[0-9]*[ ]*//"`;

    # So that you don't get locked accidentally
    if [ "shopt -u extdebug" == "$this_command" ]; then
        return 0
    fi

        # // filtering

    # Modify $this_command and then execute it
    # eval "${this_command}"

    # echo "${this_command}"


    if [[ "${this_command}" =~ ^(somethingdangercommand$|somethingdangercommand[[:space:]]) ]]; then warn "It seems You are attempting to run kind of dangerous command. Continue? [y/n]" ; ok && eval "${this_command}" || return
  elif [[ "${this_command}" =~ ^(anotherSomethingdangercommand$|anotherSomethingdangercommand[[:space:]]) ]]; then warn "It seems You are attempting to run kind of dangerous command. Continue? [y/n]" ; ok && eval "${this_command}" || return
elif [[ "${this_command}" =~ ^(rm[[:space:]][-rf]{3}$|rm[[:space:]][-rf]{3}[[:space:]]) ]]; then warn "It seems You are attempting to run kind of dangerous command. Continue? [y/n]" ; ok && eval "${this_command}" || return
  else eval "${this_command}"
    fi

        # // filtering

    return 1 # This prevent executing of original command
}

trap 'preexec_invoke_exec' DEBUG

上記コードを${HOME}/.bashrcへ追記し、試しに未定義のコマンド、somethingdangercommandコマンドを実行してみてください。未定義のコマンドなので通常だったら未定義ですよというエラーが出るところですが、プロンプトでnをエンターすることでコマンド自体が実行されないので、エラーが出ずに終了します。

trap 'preexec_invoke_exec' DEBUG ... 毎コマンド実行前にpreexec_invoke_exec()を実行

elif [[ "\{this_command}" =~ ^(rm[[:space:]][-rf]{3}|rm[[:space:]][-rf]3[[:space:]]) ]]; then warn "It seems You are attempting to run kind of dangerous command. Continue? [y/n]" ; ok && eval "${this_command}" || return ... 実行されたコマンドがrm -rfrm -frのとき警告をする

コマンドを実行する前にコマンドが正規表現に一致するかなど、フィルタをし、フィルタにかかった場合に警告をし、それ以外の場合には通常通り渡されたコマンドをevalしている感じですね。

参考

結論

rm -rfの事故はエクスクラメーション展開で起こってしまうことが多いようです。使わない場合は無効化しておくといいですね。

# disable/turn off history expansion `!` altogether
set +H
avatar

Shou Arisaka

情報技術者 / Z世代プログラマー / SaaSアプリやSNSを開発
今すぐ話そう!

15歳でWordPressサイトを立ち上げ、ウェブ領域に足を踏み入れる。翌年にはRuby on Railsを用いたマイクロサービス開発に着手し、現在はデジタル庁を支えたNext.jsによるHP作成やSaaS開発のプロジェクトに携わりながら、React.js・Node.js・TypeScriptによるモダンなウェブアプリの個人開発を趣味でも行う。
フロントエンドからバックエンドまで一貫したアジャイルなフルスタック開発を得意とし、ウェブマーケティングや広告デザインも必要に応じて担当、広告運用・SEO対策・データ分析まで行う低コストかつ高品質な顧客体験の提供が好評。
国内外から200万人を超える人々に支えられ、9周年を迎えるITブログ「yuipro」の開発者、デザイナーでありライター。現在ベータ段階の自作SNS「nyapp.buzz」を日本一の国産SNSとするべく奮闘中。

Created with Fabric.js 5.2.4 何かご質問がありますか?