bash_history: 危険なコマンドをコメントアウトする: `#`

bash_history: 危険なコマンドをコメントアウトする: `#`

「危険な」コマンドが bash 履歴に記録されるのを防ぐために、ファイルに次の行を追加しました.bashrc

HISTIGNORE='rm *:mv *:cp *:cat*>*:pv*>*'

これはうまく機能しますが、副作用があります。マシンで実行されたコマンドの完全な履歴を見ることができません。実験用に複数のマシンがあり、実行されたすべてのコマンドを確認できるようにしたいとします。history実行されたコマンドを表示するには、bash 内部を使用し、今日の日付を grep で検索します。

history | grep Sep-28

私が望んでいるのは、「危険な」コマンドもログに記録することですが、#行の先頭に を配置して、誤って履歴からコマンドを実行した場合でも、損害が発生しないようにすることです。

これが可能かどうかは分かりません。

更新と説明:

これが私にとって問題となる主な理由は、通常、複数の端末からマシンに接続しており、1つの端末で実行されたコマンドは他の端末の履歴に即座に読み込まれるためです。これは次のように実現されます。

PROMPT_COMMAND="history -a; history -c; history -r"

ターミナルを 2 つ開いているとします。1 つではcat /dev/foo > file.out、何らかのプロセスを実行しています。2 つ目では、 で進行状況を確認します。と(つまり、履歴の最後のコマンド)を押して、ls -lAhF繰り返します。最初のコマンドが終了するとすぐに、履歴の最後のコマンドは ではなくなり、 になります。注意しないと、cat を再度起動して、file.out を上書きしてしまいます。lsUpENTERlscat /dev/foo > file.out

私が実現したいのは、cat コマンドの前に , を付けて#、実行されないようにすることです。ただし、履歴には残り、コメントを解除することで再利用できます (長いコマンドの場合)。

答え1

あなたの質問に正確に答えるつもりはありませんが、問題に対する別の解決策を提示できるかもしれません。

私が正しく理解しているなら、入力時に起こりうる間違い、たとえば履歴内の前のコマンドによって保持したいものが削除されてしまうことを懸念している!rmのだと思います。rm

この場合、素敵なバッシュオプションは ですhistverifyshopt -s histverify、および感嘆符 でコマンドを呼び出すと!、そのコマンドはすぐには実行されませんが、readline にロードされ、実行するかどうかを決定できます。また、編集することもできます。

それを試してみてください:

  • それなしhistverify

    $ touch some_foo
    $ rm some_foo
    $ touch some_foo
    $ !rm
    rm some_foo
    $ # oooops in fact I'd've like to keep it this time
    
  • histverify

    $ shopt -s histverify
    $ touch some_foo
    $ rm some_foo
    $ touch some_foo
    $ !rm
    $ rm some_foo <cursor here>
    

    この場合、カーソルは行の末尾に配置され、再度起動したり、編集したりする準備が整います。

このオプションが気に入ったら、次の場所に追加してください.bashrc:

shopt -s histverify

答え2

次のようなことができます:

fixhist() {
   local cmd histnum
   cmd=$(HISTTIMEFORMAT=/ history 1)
   histnum=$((${cmd%%[*/]*}))
   cmd=${cmd#*/} # remove the histnum
   case $cmd in
     (rm\ *|mv\ *|...)
       history -d "$histnum" # delete
       history -s "#$cmd"    # add back with a #
   esac
}
PROMPT_COMMAND=fixhist

それぞれのプロンプトの前に、最後の履歴エントリ(history 1)をチェックし、それが危険なのものについては、それを削除し ( )、でhistory -d再度追加します。#history -s

(当然ですが、設定を削除する必要がありますHISTIGNORE)。

しかし、その望ましくない副作用は、歴史の時間そのうちrmmv... コマンド。

これを修正するには、次の方法があります。

fixhist() {
   local cmd time histnum
   cmd=$(HISTTIMEFORMAT='<%s>' history 1)
   histnum=$((${cmd%%[<*]*}))
   time=${cmd%%>*}
   time=${time#*<}
   cmd=${cmd#*>}
   case $cmd in
     (rm\ *|mv\ *|...)
       history -d "$histnum" # delete
       HISTFILE=/dev/stdin history -r <<EOF
#$time
#$cmd
EOF
   esac
}
PROMPT_COMMAND=fixhist

今回は、最後の履歴の時間を記録し、履歴行を再度追加するために、history -rタイムスタンプを含む一時ファイル (ヒアドキュメント) を使用します。

fixhistを の前に実行する必要がありますhistory -a; history -c; history -r。残念ながら、 の現在のバージョンbashには、追加した余分な行を保存しないバグがありますhistory -a。回避策としては、代わりに次のように記述します。

fixhist() {
   local cmd time histnum
   cmd=$(HISTTIMEFORMAT='<%s>' history 1)
   histnum=$((${cmd%%[<*]*}))
   time=${cmd%%>*}
   time=${time#*<}
   cmd=${cmd#*>}
   case $cmd in
     (rm\ *|mv\ *|...)
       history -d "$histnum" # delete
       history -a
       [ -f "$HISTFILE" ] && printf '#%s\n' "$time" "$cmd" >> "$HISTFILE";;
     (*)
       history -a
   esac
   history -c
   history -r
}
PROMPT_COMMAND=fixhist

つまり、コメント化されたコマンドを HISTFILE に追加するのではなく、自分で追加history -aするということです。

答え3

ステファンからの承認された回答は、本当に素晴らしいです。以下は、パターン マッチングを少し変えた私の採用例です。

ifの代わりにステートメントを使用すると、case正規表現マッチング ( ) を使用できます[[ "$cmd" =~ <REGEX> ]]。以下のコードは次のロジックで動作します。

  • 次の場合には履歴に追加しないでください。
    • コマンドラインは 2 つ以上の連続したスペースで始まるか終わります。
      例:
      • <spc><spc>history
  • 次の場合にはコメント化されたコマンドを追加します。
    • rmまたはmvCLIスイッチを使用したコマンド
    • コマンドラインは で始まる:;か、単一のスペースで終わるか、;:
      例:
    • rm -R /tmp/somedir

    • echo "doing something dangerous I don't want to trip by accident";:

      これにも注意点がありますが、私は問題ありません。たとえば、

    • echo ";rm -something"

  • それ以外の場合は履歴に追加します
    。例:
    • rm somefile
    • echo ";rm -something"

コード

_history_hook() {
# The code below is from https://unix.stackexchange.com/a/110056/138012
# history builtiun options used:  https://www.gnu.org/software/bash/manual/html_node/Bash-History-Builtins.html
# history -a : Append new (mem) lines to history (file)
# history -d : Delete range (mem)
# history -c : Clear history list (mem) ++
# history -r : Read (file) append to (mem) ++
# ++ needed to circumvent a bug issue.
   local cmd time histnum
   cmd=$(HISTTIMEFORMAT='<%s>' history 1)
   histnum=$((${cmd%%[<*]*}))
   time="${cmd%%>*}"
   time=${time#*<}
   cmd="${cmd#*>}"
   # different behaviors for different patterns
   if [[ "$cmd" =~ ^\ \ +.*|\ +\ $ ]]; then
   # delete only (two spaces - leading or trailing)
       history -d "$histnum" # delete
   elif [[ "$cmd" =~ ((^|;[[:space:]]*)(rm|mv)\ +-)|^\ |^:\;|\;:$ ]]; then
   # rewrite history as a 'safe version'
       history -d "$histnum" # delete
       history -a
       [ -f "$HISTFILE" ] && printf '# %s\n' "$time" "$cmd" >> "$HISTFILE"
   else
   # append 'as usual'
       history -a
   fi
   # rewriting history file
   history -c
   history -r
}

export HISTCONTROL=erasedups # This does not seem to work right now - need to fix this
export PROMPT_COMMAND="$PROMPT_COMMAND; _history_hook"

関連情報