BASH の履歴コマンドから特定の文字列を含むすべてのエントリを削除するにはどうすればよいですか?

BASH の履歴コマンドから特定の文字列を含むすべてのエントリを削除するにはどうすればよいですか?

特定の文字列を含む bash シェルの履歴からすべてのエントリを削除するコマンドまたは方法はありますか? これは、パスワードを含む履歴内のコマンドを削除するのに役立ちます。各履歴エントリを番号で削除できることはわかっていますが、問題は、一度に 1 つのエントリしか削除されず、新しいエントリを削除するたびに番号を取り出す必要があることです。

例えば、履歴コマンドにはパスワード abcabc を含む 5 つのエントリが表示され、文字列 abcabc を含むすべてのエントリを履歴コマンドから削除したい場合

975  2019-03-15 11:20:30 ll
  976  2019-03-15 11:20:33 ll cd
  977  2019-03-15 11:20:36 ll CD
  978  2019-03-15 11:20:45 chown test1:test1 CD
  979  2019-03-15 11:20:53 chown test1:test1 ./CD
  980  2019-03-15 11:20:57 chown test1:test1 .\CD
  981  2019-03-15 11:22:04 cd /tmp/logs/
  982  2019-06-07 10:36:33 su test1
  983  2019-08-22 08:35:10 su user1
  984  2019-08-22 08:35:15 /opt/abc/legacy.exe -password abcabc
  985  2019-09-24 07:20:45 cd /opt/test1/v6r2017x
  986  2019-09-24 07:20:46 ll
  987  2019-09-24 07:21:18 cd /tmp/
  988  2019-09-24 07:21:19 ll
  989  2019-09-24 07:21:24 cd linux_a64/
  990  2019-09-24 07:21:25 /opt/abc/legacy.exe -password abcabc
  991  2019-09-24 07:24:03 cd  build/
  992  2019-09-24 07:24:04 ll
  993  2019-09-24 07:24:07 cd ..
  994  2019-09-24 07:24:10 /opt/abc/legacy.exe -password abcabc
  995  2019-09-24 07:24:15 cd someapp/bin
  996  2019-09-24 07:24:21 ll
  997  2019-09-24 07:24:33 cd .
  998  2019-09-24 07:24:35 cd ..
  999  2019-09-24 07:24:36 ll

次のコマンドを試しましたが、以下のようなエラーが発生しました。

servername:~ # sed -i 'g/abcabc/d' /home/user1/.bash_history
sed: -e expression #1, char 2: extra characters after command

期待値: エラーはなく、文字列 abcabc を含むすべてのエントリが削除されるはずです。

答え1

@cprn の回答と似ていますが、awkではなくcutと関数を使用しています。

function del_word_history () {
  if [ -z "${1}" ]; then return false; fi
  mapfile -t result <<< "$(history | grep "${1}" | tac | awk '{print $1}')"
  for idx in "${result[@]}"; do
    history -d $idx
  done
}
  • tac出力の行を反転します(一部の投稿では、すべてのシステムに共通ではないと指摘されています)
  • [-z $1 ]:true関数に渡される最初の引数 ( $1) が空の場合。
  • mapfile -tマニュアルページ;いくつかのガイド):stdin配列変数に読み込みます ( result)
    • "${result[@]}"配列を返す
  • awk '{print $1}'GNU ユーザーガイド簡単なガイドマニュアルページ): 一致の識別と処理の両方に使用されます。空白で区切られたデータを分割し (デフォルト)、変数に格納します$n
  • '{pring $1}'読むprint最初の単語に対してアクションを実行する

使用法:

del_word_history history
del_word_history abcabc
history -w

以下はよりクリーンなソリューションこれは、複数行のエントリの他の単語ではなく、idxの番号を取得したかどうかを確認します。history

function del_word_history () {
  if [ -z "${1}" ]; then return false; fi
  mapfile -t lines <<< "$(history | grep "${1}" | tac)"
  for line in "${lines[@]}"; do
    idx="$(printf '%s' "${line}" | awk '{ if ($1 ~ /^[0-9]+$/) { print $1 } }')"
    if [ -n "${idx}" ]; then
      echo "removing line ${idx}"
      history -d $idx
    fi
  done
}

主な違いは次のとおりです。

  • 配列変数の全行を取得するlinesには、mapfile
  • awk '{ if ($1 ~ /^[0-9]+$/) { print $1 }}'各行の最初の単語が数字であるかどうかをチェックし、それがstdout同じ最初の単語であるかどうかを確認します(そして変数を取得しますidx)。
  • [ -n ${idx}" ] just checks ifidx` は空ではありません

答え2

古いことは承知していますが、次のようにループで実行しました。

for ln in $( history | grep my_phrase | cut -f2 -d' '); do
    history -d $ln
done
  • $(...)- コマンド置換、戻りstdout...
  • cut -f2 -d' '- スペースで区切られた出力の2列目、つまり行番号をカットします
  • history -d $ln- 履歴から指定された行番号を削除します

簡単にコピー&ペーストできるワンライナー:

for ln in $( history | grep my_phrase | cut -f2 -d' '); do history -d $ln; done

答え3

sedを使用して からエントリを削除できます.bash_history
ただし、これは CLI でパスワードを再度入力するのを避けるためのパスワードなので.bashrc、ファイルを編集して の行がまたはHISTCONTROLに設定されていることを確認することをお勧めします。これにより、 にコマンドを入力して、スペースで始まる場合は履歴に残さなくなります。 ignorebothignorespacebash

promtpt:$ sed -i '/abcabc/d' /home/user/.bash_history

$とコマンドの間にスペースがあることに注意してください。これにより、bash 履歴でその行が無視されます。

-i: はファイルをインラインで編集することを意味します。
d最後に、 の間にある式を含む行を削除することを意味します/

このソリューションは Linux でのみ機能しますGNU sed

関連情報