Работаем с историей команд

Как известно, лень человеческая - помимо слабости, является одним из основных двигателей прогресса. Не чужды ей и простые смертные линуксоиды и юниксоиды вместе взятые. Итак, как помочь человеку попавшему в дебри командного интерпретатора и облегчить его труд и усилить во сто крат силу его? Ответ на сей вопрос прост - надо дать ему в руки, и научить пользоваться таким полезным инструментом, как история команд. Сей инструмент в той или иной мере присутствует во многих программах и системах. В том же многострадальном MS-DOS, было жалкое подобие истории введенных команд, история также присутствовала в NC, FAR и других командных оболочках. Но там они ни в какое сравнение не идут с возможностями присутствующими в любом мало мальски распространенном shell под linux или unix. Далее я буду описывать работу c bash, хотя на сколько мне известно tcsh, csh и некоторые другие интерпретаторы имеют сходный набор команд для работы с историей.

Итак начав работу с командной строкой bash, я обнаружил что с помощью клавиш перемещения курсора можно перемещаться по списку ранее введенных команд. Так когда мне нужна была некоторая команда из ранее введенных я жал клавишу „стрелка вверх“ до тех пор пока нужная мне не появлялась в командной строке, потом я ее корректировал нужным мне образом и жал „Enter“ для ее выполнения. Это конечно значительно экономило время однако, как оказалось, не было наиболее эффективным способом работы.

Итак, все по порядку. Для просмотра списка ранее введенных команд в bash - имеется команда history. По умолчанию она выводит список команд хранящийся в истории. Размер данного списка определяется переменными окружения HISTSIZE - размер списка хранящегося в памяти интерпретатора, а HISTFILESIZE - максимальное количество команд хранящихся в файле истории. По умолчанию этоn файл ~/.bash_history, а его размер - 500 команд. Если вы желаете хранить историю в другом файле, то нужно в .bashrc, задать команду - HISTFILE=~/.vasya_history. Я для себя переопределяю только размер списка команд и размер файла истории, устанавливая их значения в 1000 команд. Итак введя:
        $ history

            1  history | less
            2  hg test
            3  test 333
            4  lynx www.yahoo.com
            5  cat /etc/profile.d/colorls.sh
            6  vim .screen
            7  vim .screenrc
                ..........................
          305  man bash
          306  man vfork
          307  hg lynx
          308  cd txt/everyday/
          309  vim history.txt 
          310  histroy


Отсюда видно, что в истории на данный момент находится 310 команд, конечно они все на экране не поместятся, посему если вам надо только последние 20 команд, то можно набрать:
        $ history 20

          295  vim lib/advhist.sh 
          296  getpg
          297  vim .bash_logout 
          298  vim .bashrc
          299  cat .lynxrc
          300  vim .lynxrc
          301  ls lib
          302  cat .bashrc
          303  getmail
          304  ls
          305  man bash
          306  man vfork
          307  hg lynx
          308  cd txt/everyday/
          309  vim history.txt 
          310  histroy
          311  history
          312  history 
          313  fg
          314  history 20 

Таким образом получим только последние 20 команд. Каждая команда имеет свой номер, с помощью которого к ней можно обратится. Если нам надо повторить 302 команду, то просто печатаем:
        $ !302
        cat .bashrc
        # .bashrc

        # User specific aliases and functions
        if [ "$PS1" -a -f ~/lib/advhist.sh ]; then
                . ~/lib/advhist.sh
        fi
        # Source global definitions
        if [ -f /etc/bashrc ]; then
                . /etc/bashrc
        fi
        umask 066

Здесь сначала печатается команда под номером 302 - cat .bashrc, а затем результат ее выполнения.

Приведу список команд для работы с историей:
  • !! - ссылается на предыдущую команду;
  • !n - ссылается на команду под номером n;
  • !-n - ссылается на команду по номером „текущая минус n“;
  • !string - ссылается на команду, начинающуюся с string;
  • !?string[?] - ссылается на команду, содержащую строку string;
  • ^string1^string2[^] - „быстрая замена“, заменяет первое вхождение строки string1 в предыдущей команде, на string2, после чего выполняет полученную команду.
Приведу несколько примеров по использованию вышеприведенных команд. Я часто выполняю приблизительно следующую последовательность:
        $ locate diald.conf
        /etc/diald.conf
        $ cat /etc/diald.conf
        .......


Первой командой я нахожу требуемый файл, а второй вывожу его содержимое. Сей короткий пример можно автоматизировать следующим образом:
        $ locate diald.conf
        /etc/diald.conf
        $ cat `!!`
        cat `locate diald.conf`
        mode ppp
        accounting-log /tmp/dialdlog
        .....

Таким образом я избегаю повторного ввода имени файла. `!!` означает - выполнить предыдущую команду и подставить ее результат в качестве параметра cat. Результирующая команда после подстановки из перечня печатается сразу за командной строкой: cat `locate diald.conf`.После чего выводится результат выполнения команды. На мой взгляд весьма удобно.

Идем дальше:
        $ history 10
        324  hg '
        325  hg `
        326  locate diald.c
        327  locate diald.conf
        328  cat `locate diald.conf`
        329  getmail
        330  history 10
        331  ls
        332  ps
        333  history 10
        $ !328
        cat `locate diald.conf`
        mode ppp
        accounting-log /tmp/dialdlog
        ..........

Данная команда выполняет 328 команду перечня. Другой способ обратится к этой же команде:
        $ !-6
        cat `locate diald.conf`
        mode ppp
        accounting-log /tmp/dialdlog
        ..........


Здесь используется обратная нумерация, то есть номер команды вычисляется как текущая минус 6.

Следующий способ, это когда я помню что команда начиналась со строки cat, чтобы ее повторить я печатаю следующее:
        $ !cat
        cat `locate diald.conf`
        mode ppp
        accounting-log /tmp/dialdlog
        ..........

А если я не помню названия начала команды, но помню ее середину diald, то тогда набираем:
        $ !?diald
        cat `locate diald.conf`
        mode ppp
        accounting-log /tmp/dialdlog
        ..........

Следующий интересный момент касается исправления неверно введенных команд или их корректировки:
        $ cta /etc/diald.conf
        bash: cta: command not found
        $ ^ta^at
        cat /etc/diald.conf
        mode ppp
        accounting-log /tmp/dialdlog
        ...............

В результате опечатки первая команда была введена с ошибкой, вместо cat, было введено cta, естественно интерпретатор не нашел такой команды, о чем и вывел предупреждение. Вторая команда ^ta^at^ - исправляет ошибку, делая замену ta на at. Это намного удобнее чем вызывать предыдущую команду на экран потом подводить курсор под нужный символ, а затем редактировать команду с помощью вставки/удаления символов. Данная возможность особенно удобна при удаленной работе с telnet на медленных линиях.

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

Пример:
        $ cat /etc/diald.conf
        mode ppp
        accounting-log /tmp/dialdlog
        .........
        $ vim !$


Первой командой cat мы выводим содержимое файла, например для того, чтобы удостоверится что это нужный нам файл. Удостоверившись я хочу его отредактировать, для того, чтобы повторно не набирать имя файла пишу vim !$ - что после подстановки преобразуется в vim /etc/disld.conf. Кратко опишу основные операторы обращения к различным частям предыдущей команды:
  • $ - последний аргумент командной строки;
  • ^ - первый аргумент командной строки;
  • 0 - нулевое слово, другими словами имя команды;
  • N - N-ый аргумент командной строки;
  • x-y - аргументы с x по у командной строки;
  • -y - сокращенное обращение к 0-y;
  • * - все аргументы, синоним 1-$;
  • x* - синоним x-$, другими словами аргументы с номера x до конца строки;
  • x- - синоним x*, но не включает последний аргумент.

Перед всеми описанными операторами, кроме ^ $ * %, необходимо ставить двоеточие.

Для ясности приведу несколько примеров:
        $ echo 1 2 3 4 5 6 7 8
        1 2 3 4 5 6 7 8
        $ echo !$
        echo 8
        8
        $ echo 1 2 3 4 5 6 7 8
        $ echo !*
        echo 1 2 3 4 5 6 7 8
        1 2 3 4 5 6 7 8
        $ echo !:3*
        echo 3 4 5 6 7 8
        3 4 5 6 7 8
        $ echo !^
        echo 3
        3
        $ echo !-2:-3
        echo echo 3 4 5 
        echo 3 4 5

Я наиболее часто использую операторы !^, !$, !* - которые ссылаются на первый аргумент, на последний аргумент и на все аргументы предыдущей команды соответственно.

Еще нужно упомянуть о так называемых модификаторах, подробно о них можно почитать на страницах руководства. Но об одном из них я скажу отдельно. Этот заслуживающий внимания модификатор - :p. Он говорит интерпретатору о том, что полученную в результате подстановки команду не надо выполнять, а только напечатать. Например:
        $ echo 1 2 3 4 5 6 7
        $ !!
        echo 1 2 3 4 5 6 7
        1 2 3 4 5 6 7 
        $ !!:p
        echo 1 2 3 4 5 6 7 
        $ echo !-2:p
        echo echo 1 2 3 4 5 6 7
        $

Как можно заметить - последняя и предпоследняя команды echo, не были выполнены, так как использовался модификатор :p. Вместо этого были выведены результаты подстановки.

Вот в общих чертах и все. Что я имел сказать - я сказал.

Работаем с историей команд
http://pc-inform.ru/articles/Rabotaem_s_istoriej_komand.html
-