Вопрос или проблема
Существует ли команда или способ удалить все записи из истории 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-й столбец из вывода, разделенного пробелами, т.е. номер строки.tac
–cat
задом наперед, печатает строки в обратном порядке.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 или прямого редактирования истории — это эффективные способы очистки истории команд. Однако, всегда стоит учитывать безопасность при работе с паролями и другой конфиденциальной информацией.