Команда `history` в Linux: Повний посібник з історії Bash
Команда `history` у Linux є вбудованою утилітою оболонки Bash, яка записує, відображає та керує кожною командою, виконаною в сеансі термінала. Вона зчитує та записує дані до `~/.bash_history` — текстового файлу в домашньому каталозі кожного користувача, що дозволяє відтворювати, шукати, повторно виконувати та перевіряти команди між сеансами без їх повторного введення.
Для системних адміністраторів і досвідчених користувачів історія Bash — це не просто зручна функція, а операційний журнал аудиту, інструмент налагодження та засіб підвищення продуктивності. Розуміння її внутрішньої роботи, змінних конфігурації та наслідків для безпеки відрізняє звичайних користувачів від інженерів, які отримують максимальну користь від командного рядка.
Як працює історія Bash зсередини
Коли ви відкриваєте сеанс термінала, Bash завантажує вміст `~/.bash_history` до списку в оперативній пам’яті. Під час виконання команд вони додаються до цього буфера в пам’яті. Коли сеанс завершується нормально (через `exit` або `logout`), буфер записується назад до `~/.bash_history` відповідно до правил, визначених змінними середовища.
Ця архітектура має важливе наслідкове значення: якщо ваш сеанс завершується аварійно (відключення живлення, розрив SSH-з’єднання, `kill -9`), команди з цього сеансу можуть ніколи не бути записані на диск. Це є поширеним джерелом плутанини, коли адміністратори втрачають слід команд, виконаних під час перерваного сеансу.
Дві опції оболонки змінюють цю поведінку запису при виході за замовчуванням:
- `shopt -s histappend` — додає нову історію до `~/.bash_history` замість перезапису. Це необхідно в середовищах з кількома сеансами.
- `PROMPT_COMMAND='history -a'` — змушує Bash додавати останню команду до файлу історії після кожного запрошення, забезпечуючи збереження в реальному часі та видимість між терміналами.
Без `histappend` перемагає остання закрита оболонка — вона перезаписує файл історії, мовчки відкидаючи записи з усіх інших паралельних сеансів.
Базове використання команди `history`
Відображення повної історії команд
“`bash
history
“`
Виводить пронумерований список збережених команд. Число ліворуч — це індекс історії, який використовується для позначень подій.
Відображення певної кількості останніх команд
“`bash
history 20
“`
Показує останні 20 команд. Корисно, коли потрібно швидко переглянути останню активність без прокручування сотень записів.
Негайний запис історії поточного сеансу до файлу
“`bash
history -w
“`
Примусово записує буфер історії з оперативної пам’яті до `~/.bash_history`. Використовуйте це перед закриттям критичного сеансу, щоб нічого не втратити.
Зчитування історії з файлу до поточного сеансу
“`bash
history -r
“`
Перезавантажує `~/.bash_history` до пам’яті поточного сеансу. Корисно, коли потрібно отримати доступ до команд, введених в іншому вікні термінала під час того самого входу в систему.
Відтворення та повторне виконання команд
Позначення подій за допомогою `!`
Синтаксис позначень подій у Bash дозволяє безпосередньо повторно виконувати команди з історії за посиланням:
| Позначення | Поведінка |
|---|
| — | — |
|---|
| `!!` | Повторно виконує попередню команду |
|---|
| `!n` | Виконує команду з індексом `n` в історії |
|---|
| `!-n` | Виконує команду на `n` позицій назад від поточної |
|---|
| `!string` | Виконує найновішу команду, що починається з `string` |
|---|
| `!?string?` | Виконує найновішу команду, що містить `string` будь-де |
|---|
| `!$` | Підставляє останній аргумент попередньої команди |
|---|
| `!*` | Підставляє всі аргументи попередньої команди |
|---|
Практичний приклад — повторне використання останнього аргументу:
“`bash
mkdir /var/www/myproject
cd !$
“`
`!$` розгортається до `/var/www/myproject`, позбавляючи вас від необхідності повторно вводити шлях. Це одна з найменш використовуваних, але найцінніших функцій історії Bash.
Попередній перегляд перед виконанням:
Додайте `:p` до будь-якого позначення події, щоб вивести команду без її виконання:
“`bash
!42:p
“`
Це критично важлива звичка безпеки при роботі на виробничих серверах. Завжди переглядайте деструктивні команди перед виконанням.
Позначення слів для вилучення аргументів
Окрім повторного виконання цілих команд, Bash дозволяє витягувати конкретні аргументи з записів історії:
“`bash
!!:2 # Second word (argument) of the last command
!!:1-3 # Words 1 through 3 of the last command
!ssh:$ # Last argument of the most recent ssh command
“`
Такий рівень деталізації є надзвичайно цінним при побудові складних конвеєрів або повторенні операцій з тими самими шляхами до файлів.
Клавіатурні скорочення для навігації по історії
| Скорочення | Дія |
|---|
| — | — |
|---|
| `Up Arrow` / `Ctrl+P` | Перехід до попередньої команди |
|---|
| `Down Arrow` / `Ctrl+N` | Перехід до наступної команди |
|---|
| `Ctrl+R` | Інкрементний зворотний пошук по історії |
|---|
| `Ctrl+S` | Інкрементний пошук вперед (потребує `stty -ixon`) |
|---|
| `Alt+.` | Вставляє останній аргумент попередньої команди |
|---|
| `Ctrl+G` | Скасовує поточний пошук по історії |
|---|
Примітка щодо `Ctrl+S`: За замовчуванням `Ctrl+S` активує керування потоком XON/XOFF і заморожує термінал. Щоб увімкнути пошук по історії вперед, додайте `stty -ixon` до вашого `~/.bashrc`.
Зворотний пошук за допомогою `Ctrl+R`
“`
(reverse-i-search)`git': git commit -am "fix: resolve race condition"
“`
Введіть підрядок, і Bash інкрементно знаходить найновішу команду, що його містить. Натисніть `Ctrl+R` ще раз, щоб перейти до старіших збігів. Натисніть `Enter` для виконання або `Ctrl+G` для скасування без виконання.
Для пошуку по великій кількості записів історії використовуйте конвеєр через `grep`:
“`bash
history | grep "docker run"
history | grep -E "^[[:space:]]+[0-9]+[[:space:]]+ssh"
“`
Редагування та керування записами історії
Видалення конкретного запису
“`bash
history -d 87
“`
Видаляє команду з індексом 87 зі списку в оперативній пам’яті. Щоб зробити це постійним, виконайте `history -w` для запису зміненого списку на диск.
Видалення діапазону записів
“`bash
for i in $(seq 85 90); do history -d 85; done
“`
Оскільки видалення зміщує індекси, завжди видаляйте той самий номер індексу в циклі, а не збільшуйте його.
Очищення всієї історії в оперативній пам’яті
“`bash
history -c
“`
Очищає буфер історії поточного сеансу. Це не торкається `~/.bash_history` на диску.
Повне видалення всієї історії
“`bash
history -c && history -w
“`
Очищає буфер у пам’яті, а потім записує порожній буфер до `~/.bash_history`, фактично скорочуючи файл. Це правильна двоетапна послідовність — використання лише `> ~/.bash_history` не очищає буфер у пам’яті, тому файл може бути повторно заповнений при виході із сеансу.
Налаштування історії Bash: змінні середовища
Вся поведінка історії визначається змінними середовища, які зазвичай встановлюються в `~/.bashrc` (інтерактивні оболонки без входу) або `~/.bash_profile` / `~/.profile` (оболонки з входом). Зміни набувають чинності після завантаження файлу:
“`bash
source ~/.bashrc
“`
`HISTSIZE`
Визначає, скільки команд зберігається в пам’яті під час активного сеансу.
“`bash
export HISTSIZE=10000
“`
Встановлення значення `0` повністю вимикає історію в пам’яті. Встановлення `-1` (у Bash 4.3+) робить її необмеженою.
`HISTFILESIZE`
Визначає максимальну кількість рядків, що зберігаються в `~/.bash_history` на диску.
“`bash
export HISTFILESIZE=20000
“`
Коли файл перевищує це обмеження, Bash видаляє найстаріші записи. Для середовищ з вимогами відповідності встановіть велике значення та поєднайте з ротацією журналів.
`HISTCONTROL`
Визначає правила фільтрації для команд, що записуються.
| Значення | Поведінка |
|---|
| — | — |
|---|
| `ignoredups` | Пропускає послідовні дублікати команд |
|---|
| `ignorespace` | Пропускає команди з пробілом на початку |
|---|
| `ignoreboth` | Поєднує обидва варіанти вище |
|---|
| `erasedups` | Видаляє всі попередні входження команди перед додаванням нової |
|---|
“`bash
export HISTCONTROL=ignoreboth
“`
Приклад використання `ignorespace` для безпеки: Додайте пробіл на початку будь-якої команди, що містить пароль або секрет, щоб запобігти її записуванню:
“`bash
mysql -u root -pSuperSecretPassword
“`
Це широко використовувана практика операційної безпеки на спільних або багатокористувацьких системах.
`HISTTIMEFORMAT`
Додає мітку часу до кожного запису історії, що зберігається як рядок коментаря в `~/.bash_history`.
“`bash
export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "
“`
Приклад виводу:
“`
487 2024-11-14 09:32:17 systemctl restart nginx
488 2024-11-14 09:32:45 tail -f /var/log/nginx/error.log
“`
Мітки часу є необхідними для криміналістики після інцидентів у середовищах VPS Хостингу та виділеної інфраструктури. Без них ви знаєте *що* було виконано, але не *коли*.
`HISTIGNORE`
Список шаблонів glob, розділених двокрапкою. Команди, що відповідають будь-якому шаблону, не зберігаються в історії.
“`bash
export HISTIGNORE="ls:ll:la:cd:pwd:exit:clear:history"
“`
Це запобігає засміченню вашої історії тривіальними командами та розбавленню результатів пошуку. Ви також можете використовувати символи підстановки:
“`bash
export HISTIGNORE="*password*:*secret*:*token*"
“`
Це захід глибокого захисту — поєднайте його з `ignorespace` для максимальної гігієни облікових даних.
Повна довідкова таблиця змінних конфігурації історії Bash
| Змінна | За замовчуванням | Призначення |
|---|
| — | — | — |
|---|
| `HISTSIZE` | 500–1000 | Команди, що зберігаються в пам’яті за сеанс |
|---|
| `HISTFILESIZE` | 500–2000 | Рядки, що зберігаються в `~/.bash_history` |
|---|
| `HISTCONTROL` | (не встановлено) | Правила фільтрації для записуваних команд |
|---|
| `HISTTIMEFORMAT` | (не встановлено) | Формат мітки часу, що додається до записів |
|---|
| `HISTIGNORE` | (не встановлено) | Шаблони glob для команд, що виключаються |
|---|
| `HISTFILE` | `~/.bash_history` | Шлях до файлу історії |
|---|
| `histappend` (shopt) | вимк. | Додавання або перезапис при виході із сеансу |
|---|
Спільна історія між кількома сеансами термінала
За замовчуванням кожен сеанс Bash підтримує власний ізольований буфер історії. Команди, введені в Терміналі A, невидимі для Термінала B, доки обидва сеанси не закриються і файл не буде записано. Для адміністраторів, які одночасно керують кількома SSH-сеансами на Виділених Серверах, це створює прогалини в операційному журналі.
Рекомендована конфігурація для спільної історії між сеансами в реальному часі:
“`bash
~/.bashrc
export HISTSIZE=100000
export HISTFILESIZE=100000
export HISTCONTROL=ignoreboth
export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "
shopt -s histappend
PROMPT_COMMAND='history -a; history -c; history -r'
“`
Що це робить:
- `history -a` — додає останню команду до файлу
- `history -c` — очищає буфер у пам’яті
- `history -r` — перезавантажує файл до пам’яті
Після кожної команди кожен сеанс термінала бачить повну, уніфіковану історію з усіх активних сеансів. Компроміс — незначне навантаження на виконання `PROMPT_COMMAND`, яке на практиці є несуттєвим.
Ефективний пошук по історії: розширені техніки
`fzf` — нечіткий пошук по історії
Інструмент `fzf` перетворює пошук по історії з лінійного сканування на інтерактивний інтерфейс нечіткого пошуку:
“`bash
Install fzf (Debian/Ubuntu)
sudo apt install fzf
Bind Ctrl+R to fzf-powered history search
Add to ~/.bashrc:
[ -f ~/.fzf.bash ] && source ~/.fzf.bash
“`
Після налаштування `Ctrl+R` відкриває повноекранний нечіткий пошук по всій вашій історії. Це особливо потужно при великих файлах історії (10 000+ записів), де `grep` стає незручним.
Вилучення історії для скриптів
“`bash
Export all unique commands containing "iptables" to a script
history | grep iptables | awk '{$1=""; print $0}' | sort -u > iptables_audit.sh
“`
Цей шаблон корисний для відновлення інструкцій з ad-hoc команд, виконаних під час реагування на інциденти.
Міркування безпеки щодо історії Bash
Історія Bash — це інструмент з двома гострими кінцями. Вона прискорює законні робочі процеси, але також є значною поверхнею атаки.
Основні ризики та заходи їх зменшення:
- Розкриття облікових даних: Паролі, передані як аргументи командного рядка (наприклад, `curl -u admin:password`), зберігаються у відкритому тексті в `~/.bash_history`. Натомість використовуйте `ignorespace`, `HISTIGNORE` або змінні середовища.
- Криміналістика ескалації привілеїв: Зловмисники, які отримують доступ до оболонки, зазвичай читають `~/.bash_history` для розуміння середовища, виявлення облікових даних та визначення цінних цілей. Встановіть обмежувальні дозволи: `chmod 600 ~/.bash_history`.
- Підробка історії: Скомпрометований користувач може виконати `history -c && history -w` для видалення всіх доказів. Для цілей аудиту на виробничих системах розгляньте журналювання команд на основі `auditd` або `syslog`, яким користувач не може маніпулювати.
- Ізоляція історії root: Історія користувача root зберігається в `/root/.bash_history`. Переконайтеся, що цей файл не доступний для читання всім і включений до вашої резервної копії та сфери аудиту.
Для середовищ з суворими вимогами до аудиту команд — таких як інфраструктура, що відповідає PCI-DSS або SOC 2 — однієї лише історії Bash недостатньо. Поєднайте її з аудитом на рівні ядра через `auditd` та централізованою відправкою журналів.
Порівняння історії Bash з альтернативними системами історії оболонок
| Функція | Історія Bash | Історія Zsh | Історія Fish |
|---|
| — | — | — | — |
|---|
| Файл історії за замовчуванням | `~/.bash_history` | `~/.zsh_history` | `~/.local/share/fish/fish_history` |
|---|
| Підтримка міток часу | Через `HISTTIMEFORMAT` | Вбудована | Вбудована (формат YAML) |
|---|
| Обробка дублікатів | Опція `HISTCONTROL` | Опція `HIST_IGNORE_DUPS` | Автоматична дедублікація |
|---|
| Спільна історія між сеансами | Вручну (`PROMPT_COMMAND`) | Опція `INC_APPEND_HISTORY` | Автоматично (спільна за замовчуванням) |
|---|
| Інтерфейс пошуку | `Ctrl+R` (лінійний) | `Ctrl+R` (лінійний) | З підсвічуванням синтаксису, контекстно-залежний |
|---|
| Максимальний розмір історії | Змінна `HISTFILESIZE` | Змінна `SAVEHIST` | Без жорсткого обмеження |
|---|
| Механізм блокування | Відсутній (можливі гонки станів) | Підтримується блокування файлів | На основі SQLite (атомарні записи) |
|---|
Основним обмеженням історії Bash є відсутність вбудованого блокування, що може спричиняти гонки станів при одночасному записі з кількох сеансів. Zsh і Fish вирішують це більш елегантно на рівні оболонки.
Практична конфігурація для виробничих середовищ
Нижче наведена перевірена конфігурація історії `~/.bashrc`, придатна для виробничих Linux-серверів, включаючи ті, що працюють з VPS з cPanel або власними панелями керування:
“`bash
— Bash History Configuration —
export HISTSIZE=50000
export HISTFILESIZE=50000
export HISTCONTROL=ignoreboth:erasedups
export HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S "
export HISTIGNORE="ls:ll:la:cd:pwd:exit:clear:bg:fg:jobs"
export HISTFILE=~/.bash_history
Append to history file; don't overwrite
shopt -s histappend
Save and reload history after each command
PROMPT_COMMAND='history -a; history -c; history -r'
Enable multi-line command history as single entry
shopt -s cmdhist
Store multi-line commands with embedded newlines
shopt -s lithist
“`
`cmdhist` та `lithist` заслуговують на окрему увагу. Без `cmdhist` багаторядкова команда (наприклад, цикл `for`, введений інтерактивно) зберігається як окремі рядки, що унеможливлює її чисте повторне виконання. З увімкненим `cmdhist` та встановленим `lithist` вся конструкція зберігається як єдиний запис історії з буквальними символами нового рядка, зберігаючи її структуру.
Автоматизація робочих процесів на основі історії
Створення звіту про частоту використання команд
“`bash
history | awk '{print $2}' | sort | uniq -c | sort -rn | head -20
“`
Це показує 20 найбільш використовуваних вами команд — корисно для визначення кандидатів для псевдонімів або функцій оболонки.
Аудит використання `sudo`
“`bash
history | grep sudo | awk '{$1=""; print $0}'
“`
У середовищах Панелей керування VPS це забезпечує швидкий аудит привілейованих операцій, виконаних під час сеансу.
Відновлення хронології сеансу
“`bash
HISTTIMEFORMAT="%Y-%m-%d %H:%M:%S " history | grep "2024-11-14"
“`
Фільтрує всі команди, виконані в конкретну дату — безцінно під час аналізу після інцидентів.
Ключові технічні висновки та контрольний список рішень
Перед розгортанням конфігурації історії Bash у будь-якому середовищі перевірте наступне:
- `shopt -s histappend` встановлено — запобігає втраті історії через перезапис між паралельними сеансами
- `HISTSIZE` та `HISTFILESIZE` обидва налаштовані — встановлення лише одного залишає інший зі значенням за замовчуванням, що спричиняє несподіване скорочення
- `HISTTIMEFORMAT` увімкнено — без міток часу історія не має криміналістичної цінності
- `HISTCONTROL=ignoreboth` встановлено як мінімум — зменшує шум і запобігає записуванню команд, пов’язаних з обліковими даними
- `HISTIGNORE` виключає тривіальні команди — підтримує високе співвідношення сигнал/шум в історії
- `~/.bash_history` має `chmod 600` — запобігає читанню вашої історії команд іншими користувачами
- `cmdhist` увімкнено — забезпечує зберігання багаторядкових команд як цілісних одиниць
- `PROMPT_COMMAND` синхронізує історію в реальному часі — необхідно для середовищ з кількома сеансами
- `auditd` розгорнуто поряд — для виробничих систем, де потрібне захищене від підробки журналювання
- Облікові дані ніколи не передаються як аргументи CLI — використовуйте змінні середовища, `.netrc` або менеджери секретів
Часті запитання
Чому моя історія Bash зникає після закриття SSH-сеансу?
Зазвичай це відбувається через те, що `shopt -s histappend` не встановлено. Без цього кожен сеанс перезаписує `~/.bash_history` при виході. Якщо сеанс завершується аварійно (розрив мережі, `kill -9`), запис взагалі не відбувається. Встановіть `histappend` та `PROMPT_COMMAND='history -a'` для збереження команд у реальному часі.
Як запобігти збереженню паролів в історії Bash?
Використовуйте дві взаємодоповнювальні техніки: додайте пробіл на початку команди (потребує `HISTCONTROL=ignorespace` або `ignoreboth`) та додайте шаблони чутливих команд до `HISTIGNORE`. Для довгострокової гігієни ніколи не передавайте секрети як аргументи CLI — використовуйте змінні середовища або спеціалізовані інструменти керування секретами.
У чому різниця між `HISTSIZE` та `HISTFILESIZE`?
`HISTSIZE` визначає, скільки команд Bash зберігає в пам’яті під час активного сеансу. `HISTFILESIZE` визначає, скільки рядків зберігається в `~/.bash_history` на диску. Обидва мають бути встановлені явно — великий `HISTSIZE` з малим `HISTFILESIZE` означає, що ваша внутрішньосеансова історія багата, але більша її частина відкидається при завершенні сеансу.
Чи можна відновити видалені записи історії?
Після виконання `history -c && history -w` буфер у пам’яті очищається, а файл скорочується — стандартне відновлення неможливе. Однак якщо ваша система використовує знімки файлової системи або рішення для резервного копіювання, попередня версія `~/.bash_history` може бути відновлена зі знімка. Це ще одна причина впровадити `auditd` для захищеного від підробки журналювання на критичній інфраструктурі.
Як поділитися історією Bash між кількома одночасними сеансами термінала?
Додайте наступне до `~/.bashrc`: `shopt -s histappend` та `PROMPT_COMMAND='history -a; history -c; history -r'`. Це змушує кожен сеанс додавати свою останню команду до спільного файлу та перезавантажувати повний файл після кожного запрошення, надаючи всім активним терміналам уніфікований перегляд історії команд у реальному часі.
