「危険な」コマンドが 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 を上書きしてしまいます。ls
UpENTERls
cat /dev/foo > file.out
私が実現したいのは、cat コマンドの前に , を付けて#
、実行されないようにすることです。ただし、履歴には残り、コメントを解除することで再利用できます (長いコマンドの場合)。
答え1
あなたの質問に正確に答えるつもりはありませんが、問題に対する別の解決策を提示できるかもしれません。
私が正しく理解しているなら、入力時に起こりうる間違い、たとえば履歴内の前のコマンドによって保持したいものが削除されてしまうことを懸念している!rm
のだと思います。rm
この場合、素敵なバッシュオプションは ですhistverify
。shopt -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
)。
しかし、その望ましくない副作用は、歴史の時間そのうちrm
、mv
... コマンド。
これを修正するには、次の方法があります。
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
- コマンドラインは 2 つ以上の連続したスペースで始まるか終わります。
- 次の場合にはコメント化されたコマンドを追加します。
rm
またはmv
CLIスイッチを使用したコマンド- コマンドラインは で始まる
:;
か、単一のスペースで終わるか、;:
例:
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"