Вступление

В серверной среде работа с командной строкой занимает много времени. Часто используется оболочка bash – командная оболочка по умолчанию большинства дистрибутивов.

Вероятно, во время терминальной сессии общие команды будут повторяться часто, а вариации данных команд – еще чаще. Конечно, сначала набирать каждую команду вручную очень полезно, так как это – лишняя возможность попрактиковаться, но в какой-то момент это начинает надоедать и раздражать.

К счастью, bash-оболочка имеет некоторые довольно хорошо разработанные функции истории. Умение продуктивно использовать и управлять историей в bash позволяет тратить меньше времени на ввод команд, и  тем самым увеличивает объем выполненной работы. Как известно, среди разработчиков популярен так называемый принцип DRY (Don’t Repeat Yourself). Продуктивное использование истории в bash помогает работать с информацией согласно данному принципу.

Это руководство демонстрирует все функции на Linux Ubuntu 12.04, но почти все современные дистрибутивы Linux будут работать подобным образом.

Настройки истории в bash

Прежде чем использовать историю, нужно отредактировать некоторые настройки bash, чтобы сделать ее более применимой.

Bash позволяет редактировать количество предыдущих команд, которые нужно сохранить в истории. Для этого в bash есть две отдельные опции: параметр «HISTFILESIZE» задает количество команд, хранящихся в файле истории, а «HISTSIZE» указывает количество команд, которое сохраняется в памяти для текущей сессии.

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

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

Чтобы изменить данные опции, откройте файл «~/.bashrc» с помощью редактора:

1
nano ~/.bashrc

Найдите опции «HISTSIZE» и «HISTFILESIZE». Если значения для них установлены, измените их. Если таких параметров в данном файле нет, внесите их. Данное  руководство может обойтись 10000 строк для диска и 5000 строк, сохраненных в памяти. Это достаточно скромные значения для большинства систем, но даже их придется понизить, если они влияют на производительность:

1
2
HISTSIZE=5000
HISTFILESIZE=10000

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

Это можно обойти, установив параметр «histappend», который будет добавлять историю, а не перезаписывать её. Возможно, он уже установлен​​; в противном случае его можно активировать, добавив следующую строку:

1
shopt -s histappend

Чтобы bash вносил команды в историю сразу, не дожидаясь завершения сессии (чтобы команды одного терминала сразу же были доступны в другом), можно установить или добавить команду «history –a» для опции «PROMPT_COMMAND», которая содержит команды, которые выполняются перед каждой новой командной строкой.

Но для правильной работы такой команды нужна уловка. В файл истории нужно сразу добавить  «history –a», затем очистить текущую историю данной сессии с помощью «history –c», после этого прочесть отредактированный файл истории и вернуться в историю данной сессии с помощью «history –r».

Это выглядит примерно так:

1
export PROMPT_COMMAND="history -a; history -c; history -r; $PROMPT_COMMAND"

Завершив эту операцию, сохраните изменения и закройте файл.

Чтобы активировать изменения, выйдите из системы и войдите снова, либо используйте команду source на данный файл, набрав:

1
source ~/.bashrc

Просмотр предыдущей истории в Bash

Для просмотра истории в Bash используется команда «history». Она выводит предыдущие команды, по команде на строку. В большинстве случаев она должна вывести количество строк, установленное значением «HISTSIZE».  На данный момент команд не так много:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
history
. . .
43  man bash
44  man fc
45  man bash
46  fc -l -10
47  history
48  ls -a
49  vim .bash_history
50  history
51  man history
52  history 10
53  history

Также она выводит порядковый номер команды. Каждая команда связана с номером для удобства использования.

Вывод можно сократить, указав количество команд после «history». Например, если нужно вывести только последние 5 введенных команд, можем набрать:

1
2
3
4
5
6
history 5
50  history
51  man history
52  history 10
53  history
54  history 5

Чтобы найти в истории все команды, которые содержат определенную строку, можно просто использовать grep после символа вертикальной черты. Например, чтобы найти строки, содержащие «cd», наберите:

1
2
3
4
5
6
7
8
9
history | grep cd
33  cd Pictures/
37  cd ..
39  cd Desktop/
61  cd /usr/bin/
68  cd
83  cd /etc/
86  cd resolvconf/
90  cd resolv.conf.d/

Запуск команд из истории в Bash

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

Вызвать любую из предыдущих команд  можно, введя ее номер и поставив перед ним восклицательный знак «!». Опираясь на приведенный выше пример истории, можно быстро вывести оперативную страницу руководства, просто введя:

1
!51

Это немедленно вызовет и выполнит команду в истории под номером 51.

Можно также выполнять команды относительно текущей позиции. Это делается с помощью синтаксиса «!-n», где «n» нужно заменять номером команды, которую нужно выполнить.

К примеру, если нужно вывести и выполнить предпоследнюю набранную команду, то можно набрать «!-2». Итак, если было выведено содержимое длинного пути к каталогу, а затем введена команда echo, и теперь нужно снова вывести путь, то сессия может выглядеть следующим образом:

1
2
3
ls /usr/share/doc/manpages
echo hello
!-2             # lists the contents again

Чтобы повторно выполнить последнюю команду, вместо использования «!-1» в bash можно использовать «горячую» команду «!!», что выполняет:

1
!!

Многие используют это в случае, если они набрали команду, требующую привилегий sudo. При наборе «sudo !!» команда будет перевыполнена с привилегиями sudo. Такая сессия выглядит примерно так:

1
2
3
4
5
touch /etc/hello
touch: cannot touch `/etc/hello': Permission denied
sudo !!
sudo touch /etc/hello
[sudo] password for demouser:

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

Прокрутка истории в Bash

Прокрутить историю в Bash, выводя каждую последующую команду в командной строке для редактирования, можно несколькими способами.

Наиболее распространенным способом является нажатие клавиши со стрелкой вверх в командной строке.

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

Вместо клавиш со стрелками можно использовать сочетания клавиш: «CTRL-p» для прокрутки истории назад, и «CTRL-n» для прокрутки вперед.

Чтобы вернуться к текущей командной строке, можно использовать «Meta->». В большинстве случаев «meta» и символ «>» заменяют комбинацию «ALT-Shift-.». Это очень полезно в случае, если текущая командная строка находится достаточно далеко.

Чтобы переместиться к первой строке истории, используйте обратный манёвр, «Meta-<». Обычно это сочетание замещает «ALT-Shift-,».

В качестве подведения итогов ниже приведен список наиболее важных клавиш перемещения по истории:

  • Клавиша со стрелкой вверх: прокрутка истории назад;
  • CTRL**—**p: прокрутка истории назад;
  • Клавиша со стрелкой вниз: прокрутка истории вперед;
  • CTRL**—**n: прокрутка истории вперед;
  • ALT**—Shift-.**: перемещение в конец истории (к последней введенной команде);
  • ALT**—Shift-,**: перемещение в начало истории (к первой введенной команде).

Поиск по истории в Bash

Хотя использование комбинации «history | grep» – самый простой способ выполнения некоторых процедур, его работа во многих ситуациях далеко не идеальна.
Bash имеет функции поиска по истории. К примеру, часто используется поиск по истории в обратном направлении (сначала выводятся самые «свежие» результаты) с помощью сочетания клавиш «Ctrl-r».

Например, можно ввести «Ctrl-r» и набрать часть предыдущей команды. Необходимо набрать только часть команды. Если же введенное значение совпало с  ненужной командой, можно снова нажать «Ctrl-r», чтобы вывести следующий результат.

Если нужная команда была случайно пропущена, можно изменить направление поиска при помощи комбинации «CTRL-s». Это также полезно при перемещении по истории, описанном в предыдущей главе, для поиска вперед.

Примечание: во многих терминалах, комбинация «CTRL-s» блокирует сессию терминала. То есть, при попытке использовать ее для поиска она будет «замораживать» терминал. Чтобы разблокировать сессию, просто наберите «Ctrl-q».

Эти функции приостановки и возобновления сессии в большинстве современных терминалов не нужны, потому их можно отключить, набрав:

1
stty -ixon

Теперь нужно внести это в файл «~/.bashrc», чтобы обеспечить постоянное выполнение команды.

Теперь эта комбинация поиска будет работать верно.

Поиск по введенной части команды

Распространенный способ поиска – ввести часть команды, выяснить, была ли она выполнена ранее, и затем найти ее в истории.

Как правильно выполнить поиск, используя уже внесенные в командную строку данные? Переместите курсор в начало строки при помощи «CTRL-a», затем вызовите обратный просмотр истории «CTRL-r», вставьте текущую строку в поиск с помощью «CTRL-y», и снова используйте «CTRL-r» для обратного поиска.

К примеру, необходимо обновить кэш пакета в Ubuntu. Эта команда уже была выполнена, но чтобы проверить, так ли это, можно набрать sudo:

1
sudo

На данном этапе видно, что эта команда точно уже выполнялась недавно. Теперь нажмите:

CTRL-a

Это переведет курсор в начало строки.

CTRL-r

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

CTRL-y

Это вставляет только что скопированные из командной строки части команды в поиск.

CTRL-r

Теперь нужно снова запустить обратный поиск по истории, чтобы найти команды, содержащие указанную часть.

Сначала эта процедура кажется довольно запутанной, но к ней просто нужно привыкнуть. Она чрезвычайно полезна в те моменты, когда половина сложных команд была введена, и теперь для завершения работы нужна история.

Кроме того, все эти действия можно объединить в одно:

CTRL-aryr

Продвинутое использование истории в bash

Рассмотрев основные техники использования истории, существующие в bash. Среди них:

  • !!: вывести последнюю команду;
  • **!**n: перейти к команде с порядковым номером «n»;
  • **!-**n: перейти к команде, которая была под номером «n» перед выполнением последней команды.

Определители события

Три вышеперечисленных комбинации называются определителями события. По сути, это способ вызова предыдущих команд из истории при помощи определенных критериев.

К примеру, чтобы выполнить последнюю команду «ssh», можно использовать:

1
 !ssh

Это действие ищет строки, которые начинаются с «ssh». Чтобы найти последовательность, которая находится не в начале команды, можно ввести символ «?» перед и после последовательности. К примеру, чтобы повторить последнюю команду «apt-cache search», нужно набрать:

1
!?search?

Также можно использовать вариации вывода последней команды («!!»). Выполнить быстрый поиск и замену, набрав:

1
^original^replacement^

Это вызовет последнюю команду (как «!!»), ищет совпадение со значением «original», и заменяет его значением «replacement». Затем команда будет выполнена.

Это очень удобно применять при необходимости исправить орфографические ошибки. К примеру:

1
2
3
cat /etc/hosst
cat: /etc/hosst: No such file or directory
^hosst^hosts^

Определители слов

После определителей события можно добавить символ двоеточия (:), а затем внести определитель слова, чтобы выбрать часть совпавшей команды.

Это работает путем деления команды на «слова» — участки, разделенные пробелами. Это открывает новые возможности взаимодействия с параметрами команд.

Сама команда нумеруется нулем, первый аргумент – 1, и так далее.

К примеру, можно вывести содержимое каталога, а затем изменить его таким образом:

1
2
ls /usr/share/doc/manpages
cd !!:1

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

1
cd !1

Это будет работать тем же образом.

На первый аргумент можно сослаться при помощи символа «^», а на последний аргумент – «$». Эти символы намного полезнее при работе с диапазонами, а не порядковыми номерами. К примеру, извлечь все аргументы из предыдущей команды в новую можно тремя способами:

1
2
3
!!:1*
!!:1-$
!!:*

Символ звездочки «*» применяется для обозначения вывода любых данных, кроме исходной команды. Аналогично, можно использовать номер слова, указав после него *, чтобы обозначить, что все после указанного слова должно быть также включено.

Модификаторы

Последнее, чем можно дополнить поведение вызванной строки истории – это изменение поведения вызова для управления самим текстом. Модификаторы вносятся после дополнительного символа «:» в конце строки.

К примеру, сократить путь к файлу можно при помощи модификатора «h» (что значит «head»), который удаляет путь до последнего символа слеша (/). Запомните: это не будет работать должным образом при необходимости сократить путь к каталогу, который заканчивается символом слеша.

Это часто используется при изменении файла и необходимости перейти в его каталог, чтобы выполнить операции на связанные с ним файлы.

К примеру, можно прочитать информацию об авторских правах пакета:

1
cat /usr/share/doc/manpages/copyright

После этого нужно перейти в каталог. Это можно сделать при помощи команды «cd» на строку параметров, «отрезая» имя файла в конце.

1
2
3
cd !!:$:h
pwd
/usr/share/doc/manpages

Теперь нужно снова открыть этот файл об авторских правах.

Можно также выполнить обратную процедуру, сокращая путь и используя только имя файла, при помощи модификатора «t» (что значит «tail»). Для примера можно поискать последнюю команду «cat», используя флаг «t», чтобы оставить только имя файла.

1
less !cat:$:t

Можно также просто оставить полный путь, и команда будет работать должным образом. Но иногда это не так. К примеру, если при работе с файлом, вложенным в несколько подкаталогов текущего каталога, использовать относительный путь, и изменить подкаталог с помощью модификатора «h», то использовать относительный путь к данному файлу больше не получится.

Еще один очень полезный модификатор – «r», удаляющий хвостовой суффикс вида «.xxx». Это полезно при использовании команды «tar» для распаковки файла и необходимости после этого перейти в каталог. Предполагая, что имя каталога совпадает с именем файла, можно выполнить что-то вроде:

1
2
tar xzvf long-project-name.tgz
cd !!:$:r

Если tarball использует расширение tar.gz вместо tgz, просто введите модификатор дважды:

1
2
tar xzvf long-project-name.tar.gz
cd !!:$:r:r

Похожий модификатор, «е», удаляет все, кроме хвостового расширения.

Если вызываемую команду нужно просто найти, а не выполнить, можно использовать модификатор «p», чтобы bash снова отобразил команду.

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

К примеру, команда «find» была ранее запущена на домашний каталог, а теперь ее нужно запустить из этого каталога (/). Проверить, правильно ли выполнена подстановка, можно следующим образом (при условии, что данная команда имеет №119):

1
2
3
find ~ -name "file1"    # original command
!119:0:p / !119:2*:p
find / -name "file1"

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

1
CTRL-p

Есть и более простой способ выполнения подстановки в командах: для этого можно использовать синтаксис s/original/new/

К примеру, описанное выше действие можно выполнить так:

1
!119:s/~/\//

Это заменит первый экземпляр поискового шаблона. Чтобы выполнить подстановку на каждое совпадение, используйте модификаторы «g» и «s». К примеру, чтобы создать файлы по имени » file1″, «file2» и «file3» и каталоги «dir1», «dir2», «dir3», нужно использовать:

1
2
touch file1 file2 file3
mkdir !!:*:gs/file/dir/

Итоги

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

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