Как удалить все записи, содержащие определённую строку, из истории команд в BASH?

Вопрос или проблема

Существует ли команда или способ удалить все записи из истории bash, содержащие определённую строку? Это будет полезно для удаления команд в истории, содержащих пароль. Я знаю, что мы можем удалить каждую запись в истории по её номеру, но проблема в том, что это удаляет только одну запись за раз, и мне нужно каждый раз извлекать номер для удаления новой записи.

Например, команда истории показывает 5 записей, содержащих пароль abcabc, и я хочу удалить все записи из команды истории, содержащие строку 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, должны быть удалены.

Другой вариант, аналогичный ответу @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 (страница man; некоторые руководства): считывает stdin в переменную массива (result).
    • "${result[@]}" возвращает массив.
  • awk '{print $1}' (руководство пользователя GNU, краткие руководства, страница man): используется как для идентификации соответствия, так и для его обработки. Он разделяет данные, ограниченные пробелом (по умолчанию), и сохраняет их в переменные $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 "удаление строки ${idx}"
      history -d $idx
    fi
  done
}

Основные различия:

  • мы получаем полные строки в массиве переменной lines через mapfile
  • awk '{ if ($1 ~ /^[0-9]+$/) { print $1 }}' проверяет, что первое слово каждой строки является числом, и выводит это же первое слово (и получает в переменную idx)
  • [ -n ${idx}" ] просто проверяет, что idx` не пуст

Я знаю, что это старое, но я просто сделал это в цикле следующим образом:

#!/bin/bash
for ln in $(history | grep my_phrase | awk '{$1=$1};1' | cut -f1 -d' ' | tac); do
    history -d $ln
done
history -w
  • $(...) – подстановка команд, возвращает stdout из ...
  • awk '{$1=$1};1' – удаляет пробел в начале строк истории с нумерацией, который отсутствует при переходе от 9999 к 10000 и т. д.
  • cut -f1 -d' ' – извлекает 1-й столбец из вывода, разделенного пробелами, т.е. номер строки.
  • taccat задом наперед, печатает строки в обратном порядке.
  • history -d $ln – удаляет имеющийся номер строки из истории (в памяти).
  • history -w – записывает историю из памяти на диск (заменяет старый файл).

Однострочник для простоты копирования и вставки:

 for ln in $(history | grep my_phrase | awk '{$1=$1};1' | cut -f1 -d' ' | tac); do history -d $ln; done; history -w

Обратите внимание, что в приведенном выше коде есть пробел перед for. Без него сама команда окажется в истории – если вы пытаетесь стереть, скажем, пароль, он все равно появится в команде, которую вы использовали, чтобы удалить его. Имейте в виду, чтобы это работало, вам нужно, чтобы контроллер истории игнорировал команды, начинающиеся с пробела. См. этот ответ для подробностей.

Вы можете использовать sed для удаления записей из .bash_history.
Но так как это пароль, чтобы избежать повторного ввода пароля в CLI, я рекомендую вам отредактировать ваш файл .bashrc и убедиться, что строка с HISTCONTROL установлена на ignoreboth или ignorespace. Это позволит вам вводить команды в bash и не добавлять их в историю, если они начинаются с пробела:

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

Обратите внимание на пробел между $ и командой, это сделает строку проигнорированной в истории bash.

-i: означает редактировать файл на месте.
d в конце означает удалить строку, содержащую выражение между /.

Это решение работает только на Linux с GNU sed.

Ответ или решение

Как удалить все записи, содержащие определённую строку, из истории команд в BASH

В BASH существует несколько способов удалить записи из истории команд, содержащие конкретную строку, например, пароль. Ниже приведены подробные инструкции по каждому из методов.

1. Использование функции на BASH

Можно создать функциональность для удаления строк из истории, используя встроенные команды BASH. Приведённый ниже скрипт позволяет сделать это по переданному аргументу — строке для поиска.

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 "Удаляем строку ${idx}"
            history -d $idx
        fi
    done
}

Применение функции

Чтобы удалить строки, содержащие, например, строку abcabc, выполните следующую команду:

del_word_history abcabc
history -w

Команда history -w записывает текущее состояние истории обратно в файл .bash_history.

2. Использование цикла и встроенных команд

Еще один метод, который можно использовать, чтобы удалять записи из истории, включает применение цикла:

for ln in $(history | grep my_phrase | awk '{$1=$1};1' | cut -f1 -d' ' | tac); do
    history -d $ln
done
history -w

Важно отметить, что в этом коде используется команда cut для извлечения номеров строк из истории, которые совпадают с искомой строкой. Также используется tac для обработки строк в обратном порядке, чтобы избежать проблем с изменением индексов во время удаления.

3. Прямое редактирование файла .bash_history

Вы также можете использовать утилиту sed для редактирования файла .bash_history напрямую и удаления строк, содержащих определённую подстроку:

sed -i '/abcabc/d' /home/user1/.bash_history
  • -i: редактирует файл на месте.
  • /abcabc/: ищет строки, содержащие abcabc.
  • d: удаляет найденные строки.

Этот метод подходит для Linux с установленным GNU sed.

4. Настройка истории команд

Для предотвращения случайного добавления команд, содержащих чувствительную информацию, в историю команд, вы можете настроить файл .bashrc и установить переменную HISTCONTROL:

export HISTCONTROL=ignoreboth

Это предотвратит запись команд, которые начинаются с пробела, в историю. Пожалуйста, примите во внимание, что при использовании этого метода необходимо ставить пробел перед командами, содержащими пароли.

Заключение

Выбор наиболее подходящего метода зависит от ваших потребностей. Использование функций на BASH или прямого редактирования истории — это эффективные способы очистки истории команд. Однако, всегда стоит учитывать безопасность при работе с паролями и другой конфиденциальной информацией.

Оцените материал
Добавить комментарий

Капча загружается...