Некоторое время поработав в Linux и понабирав команды в командной строке, Мефодий пришел к выводу, что в "общении" с оболочкой не помешают кое-какие удобства. Одно из таких удобств - возможность редактировать вводимую строку с помощью клавиши Backspace (удаление последнего символа), " ^W " (удаление слова) и " ^U " (удаление всей строки) - предоставляет сам терминал Linux. Эти команды работают для любого построчного ввода: например, если запустить программу cat без параметров, чтобы та немедленно отображала вводимые с терминала строки. Если по каким-то причинам в строчку на экране попало что-то лишнее, можно нажать " ^R " (redraw) - система выведет в новой строке содержимое входного буфера.
Мефодий не забыл, что cat без параметров следует завершать командой " ^D " (конец ввода). Эту команду, как и предыдущие, интерпретирует при вводе с терминала система. И она же превращает некоторые другие управляющие символы (например, " ^C " или " ^Z ") в stty. Полный список того, что можно настраивать, выдает команда stty -a:
[methody@localhost methody]$ stty -a localhost 38400 baud; rows 30; columns 80; line = 0; intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0; -parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts -ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel -iutf8 opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke
При виде столь обширных возможностей Мефодий немедленно взялся читать руководство ( man stty ), однако нашел в нем не так уж много для себя полезного. Из управляющих символов (строки со второй по четвертую) интересны " ^S " и " ^Q ", с помощью которых можно, соответственно, приостановить и возобновить выдачу на терминал (если текста вывелось уже много, а прочесть его не успеваешь). Можно заметить, что настройка erase (удаление одного символа) соответствует управляющему символу, который возвращается клавишей Backspace именно ^?". На многих терминалах клавиша Backspace возвращает другой символ - " ^H ". Если необходимо переопределить настройку erase, можно воспользоваться командой " stty erase ^H ", причем " ^H " (для удобства) разрешено вводить и как два символа: " ^ " и " H ".
Наконец, чтобы лишить передаваемый символ его управляющих функций (если, например, требуется передать программе на ввод символ с кодом 3, т. е. " ^C "), непосредственно перед вводом этого символа нужно подать команду " ^V " ( lnext ):
[methody@localhost methody]$ cat | hexdump -C Сейчас нажмем Ctrl+C [methody@localhost methody]$ cat | hexdump -C Теперь Ctrl+V, Ctrl+C, enter и Ctrl+D^C 00000000 f4 c5 d0 c5 d2 d8 20 43 74 72 6c 2b 56 2c 20 43 |Теперь Ctrl+V, C| 00000010 74 72 6c 2b 43 2c 20 45 6e 74 65 72 20 c9 20 43 |trl+C, enter и C| 00000020 74 72 6c 2b 44 03 0a |trl+D..| 00000027
Здесь Мефодий прервал, как и собирался, работу первого из cat. При этом до hexdump, cat не успел обработать ни одной строки. Во втором случае " ^C " после " ^V " потеряло управляющий смысл и отобразилось при вводе. С ключом " -C " hexdump выводит также и текстовое представление входного потока, заменяя непечатные символы точками. Так, на точки были заменены и " ^C " (ASCII-код 03 ), и возвращаемый Enter символ конца строки (ASCII-код 0a, в десятичном виде - 10 ). Ни " ^V ", ни " ^D " на вход hexdump, конечно, не попали: их, как управляющие, обработала система.
Прочие настройки stty относятся к обработке текста при выводе на терминал и вводе с него. Они интересны только в том смысле, что при их изменении работать с командной оболочкой становится неудобно. Например, настройка echo определяет, будет ли система отображать на экране все, что вводит пользователь. При включенном echo нажатие любой алфавитно-цифровой клавиши (ввод символа) приводит к тому, что система (устройство типа tty ) выведет этот символ на терминал. Настройка отключается, когда с клавиатуры вводится пароль. При этом трудно отделаться от ощущения, что ввода с клавиатуры не происходит. Еще хуже обстоит дело с настройками, состоящими из кусков вида " i ", " o ", " cr " и " nl ". Эти настройки управляют преобразованием при вводе и выводе исторически сложившегося обозначения конца строки двумя символами в один, принятый в Linux. Может случиться так, что клавиша Enter терминала возвращает как раз неправильный символ конца строки, а преобразование отключено.
Тогда вместо Enter следует использовать " ^J " - символ, на самом деле соответствующий концу строки.
Во всех случаях, когда терминал находится в непонятном состоянии - не реагирует на Enter, не показывает ввода, не удаляет символов, выводит текст "ступеньками" и т. п., рекомендуется "лечить" настройки терминала с помощью stty sane - специальной формы stty, сбрасывающей настройки терминала в некоторое пригодное к работе состояние. Если непонятное состояние терминала возникло однократно, например, после аварийного завершения экранной программы (редактора или оболочки mc ), то можно воспользоваться командой reset. Она заново настраивает терминал в полном соответствии с системной конфигурацией (указанной в файле /etc/, см. лекцию 10)
и ^J stty sane^J ".
Даже не изучая специально возможностей командной оболочки, Мефодий активно использовал некоторые из них, недоступные при вводе текста большинству утилит (в частности, ни cat, ни hexdump ). Речь идет о клавишах Стрелка влево и Стрелка вправо, с помощью которых можно перемещать курсор по командной строке, и клавише Del, удаляющей символ под курсором, а не позади него. В лекции 2 он уже убедился, что эти команды работают в bash, но не работают для cat. Более того, в простом командном интерпретаторе - sh - они тоже не работают.
Следовательно, возможности редактора командной строки специфичны для разных командных оболочек. Однако самые необходимые команды редактирования поддерживаются во всех разновидностях shell сходным образом. По словам Гуревича "во всех видах Linux обязательно есть bash, а если ты достаточно опытен, чтобы устанавливать и настраивать пакеты, можешь установить zsh, у него возможностей больше, чем может понадобиться одному человеку". Поэтому Мефодий занялся изучением документации по bash, что оказалось делом непростым, ибо в bash.info он насчитал более восьми с половиной тысяч строк. Даже про
Попытка "наскоком" узнать все про работу в командной строке принесла некоторую пользу. Во-первых, перемещаться в командной строке можно не только по одному символу вперед и назад, но и по словам: команды escF/escB или Alt+F/Alt+B соответственно (от forward и backward), работают также клавиши home и end, или, что то же самое, " ^A " и " ^E ". А во-вторых, помимо работы с одной командной строкой, существует еще немало других удобств, о которых и пойдет речь в этой лекции.
Двумя другими клавишами со стрелками - вверх и вниз - Мефодий тоже активно пользовался, не подозревая, что задействует этим весьма мощный механизм bash - работу с bash запоминает и позволяет обращаться к ним впоследствии. По стрелке вверх (можно использовать и " ^P ", previous), список поданных команд "прокручивается" от последней к первой, а по стрелке вниз (" ^N ", next) - обратно. Соответствующая команда отображается в командной строке как только что набранная, ее можно отредактировать и подать оболочке (подгонять курсор к концу строки при этом необязательно).
Если необходимо добыть из истории какую-то давнюю команду, проще не гонять список истории стрелками, а выполнить поиск с помощью команды " ^R " (reverse search). При этом выводится подсказка специального вида ("reverse-i-search"), подстрока поиска (окруженная символами ` и ' ) и последняя из команд в истории, в которой эта подстрока присутствует:
[methody@localhost methody]$ ^R | (reverse-i-search)`': i | (reverse-i-search)`i': ls i n | (reverse-i-search)`in': info f | (reverse-i-search)`inf': info o | (reverse-i-search)`info': info ^R | (reverse-i-search)`info': man info ^R | (reverse-i-search)`info': info "(bash.info.bz2)Commands For History"
Пример представляет символы, вводимые Мефодием (в левой части до " | "), и содержимое последней строки терминала. Это "кадры" работы с одной и той же строкой, показывающие, как она меняется при наборе. Набрав "info", Мефодий продолжил поиск этой подстроки, повторяя " ^R " до тех пор, пока не наткнулся на нужную ему команду, содержащую подстроку " info ". Осталось только передать ее bash с помощью Enter.
Чтобы bash записывает ее в файл .bash_history, находящийся в домашнем каталоге пользователя. Делается это в момент завершения оболочки: накопленная за время работы история дописывается в конец этого файла. При следующем запуске bash считывает .bash_history целиком. История хранится не вечно, количество запоминаемых команд в .bash_history ограничено (обычно сохраняется 500 команд, но это можно и перенастроить).
Поиск по истории - удобное средство: длинную командную строку можно не набирать целиком, а отыскать и использовать. Однако давнюю команду придется добывать с помощью нескольких " ^R " - а можно и совсем не доискаться, если она уже выбыла оттуда. Для того чтобы оперативно заменять длинные команды короткими, стоит воспользоваться сокращениями (aliases). В alias без параметров:
[methody@localhost methody]$ alias alias cd..='cd ..' alias cp='cp -i' alias l='ls -lapt' alias ll='ls -laptc' alias ls='ls --color=auto' alias md='mkdir' alias mv='mv -i' alias rd='rmdir' alias rm='rm -i'
С сокращениями Мефодий уже сталкивался в примерах, приведенных в лекции 6, где команда ls отказалась работать в согласии с теорией. Выяснилось, что по команде ls вместо утилиты /bin/ls bash запускает собственную ls --color=auto. Повторно появившуюся в команде подстроку " ls " интерпретатор уже не обрабатывает, во избежание вечного цикла. Например, команда ls -al превращается в результате в ls --color=auto -al. Точно так же любая команда, начинающаяся с rm, превращается в rm -i (interactive), что Мефодия крайне раздражает, потому что ни одно удаление не обходится без вопросов в стиле " rm: удалить обычный файл `файл'?":
[methody@localhost methody]$ unalias cp rm mv [methody@localhost methody]$ alias pd=pushd [methody@localhost methody]$ alias pp=popd [methody@localhost methody]$ pd /bin /bin ~ [methody@localhost bin]$ pd /usr/share/doc /usr/share/doc /bin ~ [methody@localhost doc]$ cd /var/tmp [methody@localhost tmp]$ dirs /var/tmp /bin ~ [methody@localhost tmp]$ pp /bin ~ [methody@localhost bin]$ pp ~ [methody@localhost methody]$ pp -bash: popd: directory stack empty
От надоедливого " -i " Мефодий избавился с помощью команды unalias, а заодно ввел сокращения для полюбившихся ему команд bash - pushd и popd. pushd и popd .cd, меняют текущий каталог. Разница состоит в том, что pushd все каталоги, которые пользователь делает текущими, запоминает в особом списке (стеке). Команда popd удаляет последний элемент этого стека и делает текущим каталогом предпоследний. Обе команды вдобавок выводят содержимое стека каталогов (то же самое делает и команда dirs ). Команда cd в bash также работает со стеком каталогов: она заменяет его последний элемент новым.
Сокращения позволяют быстро набирать команды, однако никак не затрагивают имен файлов, которые чаще всего и оказываются параметрами этих команд. Бывает, что набранной строки - пути к файлу и нескольких первых букв его имени - достаточно для однозначного указания на этот файл, потому что по введенному пути других файлов, чье имя начинается на эти буквы, просто нет. Чтобы не дописывать оставшиеся буквы (а имена файлов в Linux могут быть весьма длинными), Гуревич посоветовал Мефодию нажать клавишу Tab. И - о чудо! - bash сам достроил начало имени файла до целого (снова воспользуемся методом "кадров"):
[methody@localhost methody]$ ls -al /bin/base Tab | [methody@localhost methody]$ ls -al /bin/basename -rwxr-xr-x 1 root root 12520 Июн 3 18:29 /bin/basename [methody@localhost methody]$ base Tab | [methody@localhost methody]$ basename Tab | [methody@localhost methody]$ basename ex Tab | [methody@localhost methody]$ basename examples/ Tab | [methody@localhost methody]$ basename examples/-filename-with- -filename-with-
Дальше - больше. Оказывается, и имя команды можно вводить не целиком: оболочка достроит набираемое слово именно до команды, раз уж это слово стоит в начале командной строки. Таким образом, команду Мефодий набрал за восемь нажатий на клавиатуру (" base " и четыре Tab )! Ему не пришлось вводить начало имени файла в каталоге examples, потому что файл там был всего один.
Выполняя bash может вывести не всю строку, а только ту ее часть, относительно которой у него нет сомнений. Если дальнейшее Tab приведет к тому, что bash растерянно ^G ". Для этого не нужно запускать никаких дополнительных программ: "настоящие" терминалы имеют встроенный динамик, а screen пишет в служебной строке "wuff-wuff" ("гав-гав").Tab.
Поиск ключевого слова "completion" по документации bash выдал так много информации, что Мефодий обратился к Гуревичу за помощью. Однако тот ответил, что не использует bash, и поэтому не в состоянии объяснять тонкости его настройки. Если в bash предусмотрено несколько типов zsh их сколько угодно: существует способ запрограммировать любой алгоритм
mv,
Символы в
abc " соответствует строка abc, но не aBc или ABC, потому что большие и маленькие буквы различаются.* ", соответствует любая строка любой длины (в том числе и пустая).a, + или @, но не ab или 8888.c ").[methody@localhost methody]$ ls .bash* .bash_history .bash_logout .bash_profile .bashrc [methody@localhost methody]$ /bin/e* /bin/ed /bin/egrep /bin/ex [methody@localhost methody]$ ls *a* -filename-with- [methody@localhost methody]$ ls -dF *[ao]* Documents/ examples/ loop to.sort*
Мефодий, как это случается с новичками, немедленно натолкнулся на несколько "подводных камней", неизбежных в ситуации, когда конечный результат неизвестен. Только первая команда сработала не вопреки его ожиданиям: .bash* " был превращен командной оболочкой в список файлов, начинающихся на .bash, этот список получила в качестве параметров командной строки ls, после чего честно его вывела. С " /bin/e* " Мефодию повезло - этот /bin, начинающихся на " e ", и первым файлом в списке оказалась безобидная утилита /bin/echo. Поскольку в командной строке ничего, кроме /bin/echo была воспринята оболочкой /bin/ed, /bin/egrep и /bin/ex.
Что же касается ls *a*, то, по расчетам Мефодия, эта команда должна была выдать список файлов в текущем каталоге, имя которых содержит " a ". Вместо этого на экран вывелось имя файла из подкаталога examples... Впрочем, никакой черной магии тут нет. Во-первых, имена файлов вида " .bash* " хотя и содержат " a ", но начинаются с точки, и, стало быть, считаются скрытыми. *a* " в домашнем каталоге Мефодия bash нашел только подкаталог с именем examples - его-то он и передал в качестве параметра утилите ls. Что было выведено на экран в результате образовавшейся команды ls examples? Конечно, содержимое каталога. *[ao]* " был превращен в список файлов, чьи имена содержат " a " или " o " - Documents examples loop to.sort, а ключ " -d " потребовал от ls показывать информацию о каталогах, а не об их содержимом. В соответствии с ключом " -F ", ls расставила " / " после каталогов и " * " после исполняемых файлов.
Еще одно отличие генерации имен от стандартной обработки / ", разделяющий элементы пути, никогда не ставится в соответствие " * " или диапазону. Происходит это не потому, что искажен алгоритм, а потому, что при генерации имен / ". Например, получить список файлов, которые находятся в каталогах /usr/bin и /usr/sbin и содержат подстроку " ppp " в имени, можно с помощью /usr/*bin/*ppp* ". Однако одного шаблона, который бы включал в этот список еще и каталоги /bin и /sbin - то есть подкаталоги другого уровня вложенности - zsh " предусматривает специальный ** ", которому соответствуют подстройки с любым количеством " / ". Пользоваться им следует крайне осторожно, понимая, что при генерации имен по такому ls, а ls -R или find. Так, использование " /** " в начале
Если перед любым специальным символом стоит " \ ", этот символ лишается специального значения, экранируется: пара " \символ " заменяется командным интерпретатором на " символ " и передается в командную строку без всякой дальнейшей обработки:
[methody@localhost methody]$ echo *o* Documents loop to.sort [methody@localhost methody]$ echo \*o\* *o* [methody@localhost methody]$ echo "*o*" *o* [methody@localhost methody]$ echo *y* *y* [methody@localhost methody]$ ls *y* ls: *y*: No such file or directory
Мефодий заметил, что bash раскрывать не стал, как если бы все " * " в нем были экранированы. В самом деле, какое из двух зол меньше: изменять интерпретацию спецсимволов в зависимости от содержимого каталога, или сохранять логику интерпретации с риском превратить команду с параметрами в команду без параметров? Если бы, допустим, ls *y* превратилась бы в ls и неожиданно выдала бы содержимое всего каталога. Авторы bash (как и Стивен Борн, автор самой первой командной оболочки - sh ) выбрали более непоследовательный,
но и более zsh пошли по другому пути: в этой версии shell использование
Лишить специальные символы их специального значения можно и другим способом. В лекции 2 было рассказано, что
В лекции 5 уже упоминалось, что системный вызов fork(), создавая точную копию родительского процесса, копирует также и
Эти свойства и реализованы в понятии "
[methody@localhost methody]$ date Птн Ноя 5 16:20:16 MSK 2004 [methody@localhost methody]$ LC_TIME=C date Fri Nov 5 16:20:23 MSK 2004
В последнем примере Мефодий воспользовался подсмотренным у Гуревича приемом: присвоил некоторое значение = " внутри первого слова командной строки, приходит к выводу, что это - операция присваивания, а не имя команды, и запоминает, как надо изменить LC_TIME предписывает использовать определенный язык при выводе даты и времени, а значение " C " соответствует стандартному системному языку (чаще всего - английскому).
Если рассматривать shell в качестве высокоуровневого языка программирования, то его переменные - самые обычные строковые переменные. Записать значение в переменную можно с помощью операции присваивания, а прочесть его оттуда - с помощью операции $переменная:
[methody@localhost methody]$ A=dit [methody@localhost methody]$ C=dah [methody@localhost methody]$ echo $A $B $C dit dah [methody@localhost methody]$ B=" " [methody@localhost methody]$ echo $A $B $C dit dah [methody@localhost methody]$ echo "$A $B $C" dit dah [methody@localhost methody]$ echo '$A $B $C' $A $B $C
Как видно из примера, значение неопределенной переменной ( B ) в shell считается пустым и при echo в примере получила, как и первая, два параметра (" " и " dah "), несмотря на то, что переменная B была к тому времени определена и содержала echo получили по одному параметру. Здесь сказалось различие между одинарными и двойными кавычками в shell: внутри двойных кавычек действует
Переменные, которые командный интерпретатор bash определяет после запуска, не принадлежат bash попала в export:
[methody@localhost methody]$ echo "$Qwe -- $LANG" -- ru_RU.KOI8-R [methody@localhost methody]$ Qwe="Rty" LANG=C [methody@localhost methody]$ echo "$Qwe -- $LANG" Rty -- C [methody@localhost methody]$ sh sh-2.05b$ echo "$Qwe -- $LANG" -- C sh-2.05b$ exit [methody@localhost methody]$ echo "$Qwe -- $LANG" Rty -- C [methody@localhost methody]$ export Qwe [methody@localhost methody]$ sh sh-2.05b$ echo "$Qwe -- $LANG" Rty -- C sh-2.05b$ exit
Здесь Мефодий завел новую переменную Qwe и изменил значение LANG, доставшейся стартовому bash от программы login. В результате запущенный sh получил измененное значение LANG и никакой переменной Qwe в export Qwe эта переменная была добавлена в sh.
Во время сеанса работы пользователя login довольно богатое bash можно с помощью команды set. Большинство заранее определенных переменных используются либо самой командной оболочкой, либо утилитами системы, поэтому их изменение приводит к тому, что оболочка или утилиты начинают работать несколько иначе.
Весьма примечательна PATH. В ней содержится список каталогов, элементы которого разделяются двоеточиями. Если команда в командной строке - не собственная команда shell (вроде cd ) и не представлена в виде пути к запускаемому файлу (как /bin/ls или ./script ), то shell будет искать эту команду среди имен запускаемых файлов во всех каталогах PATH, и только в них. Точно так же будут поступать и другие утилиты, использующие или (запуск программы).
По этой причине исполняемые файлы невозможно запускать просто по имени, если они лежат в текущем каталоге, и текущий каталог не входит в PATH. Мефодий в таких случаях пользовался кратчайшим из возможных путей, " ./ " (например, вызывая ./script ):
[methody@localhost methody]$ echo $PATH /home/methody/bin:/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/usr/games [methody@localhost methody]$ mkdir /home/methody/bin [methody@localhost methody]$ mv to.sort loop script bin/ [methody@localhost methody]$ script Hello, Methody!
Пути, указанные в PATH, могут и не существовать на самом деле. Обнаружив в $PATH элемент /home/methody/bin (подкаталог bin домашнего каталога), Мефодий решил, что гораздо правильнее будет складывать исполняемые файлы не куда попало, а именно в этот каталог: во-первых, это упорядочит структуру домашнего каталога, а во-вторых, позволит запускать эти файлы по имени. Но для начала пришлось такой каталог создать. Порядок, при котором собственные программы лежат в специальном каталоге, куда безопаснее беспорядка, при котором поиск запускаемого файла по имени начинается с текущего каталога. Если в текущем каталоге окажется программа ls и этот каталог - не /bin, а, допустим, /home/shogun/dontrunme, тогда следует ожидать подвоха...
, оперируя независящими от типа терминала понятиями (вроде "очистка экрана" или "позиционирование курсора"), а процедуры из преобразуют их в управляющие последовательности конкретного терминала,
сверившись сначала с $TERM, а затем с содержимым соответствующего раздела базы данных по терминалам, которая называется terminfo .EDITOR (обычно это /usr/bin/vi, о котором речь пойдет в лекции 9). Наконец, некоторые переменные вроде UID, USER или PWD просто содержат полезную информацию, которую можно было бы добыть и другими способами.
Некоторые bash: они задают его свойства и особенности поведения. Таковы переменные семейства PS (Prompt String). В этих переменных хранится строка-подсказка, которую командный интерпретатор выводит в разных состояниях. В частности, содержимое PS1 - это подсказка, которую shell показывает, когда вводит командную строку, а PS2 - когда пользователь нажимает Enter, а интерпретатор по какой-то причине считает, что ввод командной строки не завершен (например, не закрыты кавычки). С $PS2 (символом " > ") Мефодий уже сталкивался в лекциях 2 и 7:
[methody@localhost methody]$ cd examples/ [methody@localhost examples]$ echo $PS1 [\u@\h \W]\$ [methody@localhost examples]$ PS1="--> " --> --> PS1="\t \w " 22:11:47 ~ 22:11:48 ~ 22:11:48 ~ PS1="\u@\h:\w \$ " methody@localhost:~/examples $ methody@localhost:~/examples $ methody@localhost:~/examples $ cd methody@localhost:~ $ methody@localhost:~ $
Мефодий экспериментировал с PS1, параллельно читая документацию по bash (" (bash.info)Printing a Prompt "). Оказывается, в этом командном интерпретаторе содержимое PS1 не просто подставляется при выводе - оно еще и преобразуется, позволяя выводить всякую полезную информацию: имя пользователя (соответствует подстроке " \u ", user ), имя компьютера (" \h ", host ), время (" \t ", time ), путь к текущему каталогу (" \w ", \W " (показывающую последний элемент пути, то есть собственное имя текущего каталога) на " \w ", полный путь, потому что " \w " обладает свойством выделять в полном пути домашний каталог и заменять его на " ~ ". Такое преобразование значений переменных семейства PS1 выполняется, только когда их использует bash в качестве подсказки,
а при обычной
Некогда Мефодий выучил несколько языков программирования, и уже собрался было написать кое-какие нужные программы на Си или Python, однако Гуревич его остановил. Большая часть того, что нужно начинающему пользователю Linux, делается с помощью одной правильной команды, или вызовом нескольких команд в конвейере. От пользователя только требуется оформить решение задачи в виде sh, был настоящим высокоуровневым языком программирования - если, конечно, считать все утилиты системы его операторами. При таком подходе от sh требуется совсем немного: возможность вызывать утилиты, возможность свободно манипулировать результатом их работы и несколько алгоритмических конструкций (условия и циклы).
К сожалению, программирование на shell, а также других, более мощных интерпретируемых языках в Linux, остается за рамками нашего курса. Так что, пока Мефодий читает документацию по bash и упражняется в написании bash - непрактично, так как исполняться они смогут лишь при помощи bash. Если же ограничить себя рамками sh, совместимость с которым объявлена и в bash, и в zsh, и в ash (наиболее близком по возможностям к sh ), и в других командных интерпретаторах, выполняться эти sh- подобных интерпретаторов, и не только в Linux.
Каждый процесс Linux при завершении передает родительскому
[methody@localhost methody]$ grep Methody bin/script echo 'Hello, Methody!' [methody@localhost methody]$ grep -q Methody bin/script ; echo $? 0 [methody@localhost methody]$ grep -q Shogun bin/script ; echo $? 1 [methody@localhost methody]$ if grep -q Shogun bin/script ; then echo "Yes"; fi [methody@localhost methody]$ if grep -q Methody bin/script ; then echo "Yes"; fi Yes
if запускает команду- условие, grep -q, которая ничего не выводит на экран, зато возвращает 0, если 1, если нет. В зависимости от if выполняет или не выполняет тело: список команд, заключенный между then и fi. Точка с запятой разделяет операторы в sh ; либо она, либо перевод строки необходимы перед then и fi, иначе все, что идет после grep, интерпретатор посчитает параметрами этой утилиты.
Множеством функций обладает команда test: она умеет сравнивать числа и строки, проверять ярлык объекта файловой системы и наличие самого этого объекта. У " test " есть второе имя: " [ " (как правило, /usr/bin/[ - символьная или даже /usr/bin/test), позволяющее оформлять оператор if более привычным образом:
[methody@localhost methody]$ if test -f examples ; then ls -ld examples ; fi [methody@localhost methody]$ if [ -d examples ] ; then ls -ld examples ; fi drwxr-xr-x 2 methody methody 4096 Окт 31 15:26 examples [methody@localhost methody]$ A=8; B=6; if [ $A -lt $B ] ; then echo "$A<$B" ; fi [methody@localhost methody]$ A=5; B=6; if [ $A -lt $B ] ; then echo "$A<$B" ; fi 5<6
Команда test -f проверяет, не является ли ее аргумент файлом; поскольку examples - это каталог, результат будет неудачным. Команда [ -d - то же самое, что и test -d (не каталог ли первый параметр), только последним параметром этой команды - исключительно для красоты - должен быть символ " ] ". Результат - успешный, и выполняется команда ls -ld. Команда test параметр1 -lt параметр3 проверяет, является ли параметр1 числом, меньшим, чем (less than) параметр3. В более сложных случаях оператор if удобнее записывать "лесенкой", выделяя переводами строки окончание условия и команды внутри тела (их может быть много).
Второй тип `команда` " (другой вариант - " $(команда) "). Как и
[methody@localhost methody]$ A=8; B=6 [methody@localhost methody]$ expr $A + $B 14 [methody@localhost methody]$ echo "$A + $B = `expr $A + $B`" 8 + 6 = 14 [methody@localhost methody]$ A=3.1415; B=2.718 [methody@localhost methody]$ echo "$A + $B = `expr $A + $B`" expr: нечисловой аргумент 3.1415 + 2.718 = [methody@localhost methody]$ echo "$A + $B" | bc 5.8595 [methody@localhost methody]$ C=`echo "$A + $B" | bc` [methody@localhost methody]$ echo "$A + $B = $C" 3.1415 + 2.718 = 5.8595
Сначала для арифметических вычислений Мефодий хотел воспользоваться командой expr, которая работает с параметрами командной строки. С целыми числами expr работает неплохо, и ее результат можно подставить прямо в аргумент команды echo. С действительными числами умеет работать утилита- bc ; арифметическое выражение пришлось сформировать с помощью echo и передать по конвейеру, а результат поместить в переменную C. Во многих современных командных оболочках есть встроенная целочисленная арифметика вида " $(( выражение )) ".
В языке sh много внимания было уделено удобству написания
[methody@localhost methody]$ cat > bin/two #!/bin/sh echo "Running $0: $*" $1 $3 $2 $3 [methody@localhost methody]$ chmod +x bin/two [methody@localhost methody]$ bin/two file "ls -ld" examples Running bin/two: file ls -ld examples examples: directory drwxr-xr-x 2 methody methody 4096 Окт 31 15:26 examples [methody@localhost methody]$ two "ls -s" wc "bin/two bin/loop" junk Running /home/methody/bin/two: ls -s wc bin/two bin/loop junk 4 bin/loop 4 bin/two 4 9 44 bin/two 1 5 26 bin/loop 5 14 70 итого
Как видно из примера, форма " $номер_параметра " позволяет обратиться и к нулевому параметру - команде, а вся строка параметров хранится в переменной " * ". Кроме того, свойство ls -ld " или " bin/two bin/loop ", а интерпретатору - разбить эти параметры на имя команды и ключи и два имени файла соответственно.
В sh есть и while, формат которого аналогичен if, и более удобный именно в for (список делится на слова так же, как и командная строка - с помощью
[methody@localhost methody]$ for Var in Wuff-Wuff Miaou-Miaou; do echo $Var; done Wuff-Wuff Miaou-Miaou [methody@localhost methody]$ for F in $(date); do echo -n "<$F>"; done; echo <Сбт><Ноя><6><12:08:38><2004> [methody@localhost methody]$ cat > /tmp/setvar QWERTY="$1" [methody@localhost methody]$ sh /tmp/setvar 1 2 3; echo $QWERTY [methody@localhost methody]$ . /tmp/setvar 1 2 3; echo $QWERTY 1
Во втором for Мефодий воспользовался date, каждое слово которой вывел с помощью echo -n в одну строку, а в конце команды пришлось вывести один перевод строки вручную.
Вторая половина примера иллюстрирует ситуацию, с которой Мефодий столкнулся во время своих экспериментов: все переменные, определяемые в exit, exec или какая-нибудь другая, приводящая к завершению работы интерпретатора, завершится именно текущая командная оболочка, чем сеанс работы пользователя в системе может и закончиться.
Научившись (главным образом в результате чтения документации и непрестанных экспериментов) создавать работающие
Оказалось, что настройка управляющих клавиш в bash не выглядит как bash, а ко всем программам, использующим readline. readline называется .inputrc и состоит, в основном, из команд вида " управляющая_последовательность ": функция, где управляющая_последовательность - это символы, при получении которых readline выполнит функцию работы с вводимой строкой. Список всех функций readline можно узнать у bash по команде bind -l, а список всех привязок этих функций к клавиатурным последовательностям - по команде bind -p. Мефодий вписал в .inputrc такие две строки:
"\e[5~": backward-word "\e[6~": forward-word
Упомянутые в примере функции позволяют перемещать курсор в командной строке по словам, а esc-последовательности возвращаются, соответственно, клавишами Page Up и Page Down \e " означает в .inputrc клавишу esc, то есть " ^[ ", символ с ASCII-кодом 27 ).
К одной и той же функции readline можно привязать сколько угодно управляющих последовательностей: например, клавиша home делает то же, что и " ^A ", Стрелка вверх - то же, что и " ^P ", а Del - то же, что и " ^D " (только не в пустой строке!). Таким образом отчасти решается проблема несовместимости управляющих последовательностей терминалов: если в каком-нибудь терминале другого типа Page Up или Page Down будут возвращать другие последовательности, Мефодий просто добавит в .inputrc еще одну пару команд. Правда, Гуревич советовал ему вовсе отказаться от редактирования .inputrc, а воспользоваться утилитой tput, которая обращается к переменной TERM и базе данных по терминалам terminfo и готова выдать верную для любого данного терминала информацию по kpp (key previous page) и knp
(key next page).
Выдачу tput можно "скормить" той же bind, и получить команду, которая работает на любом терминале: bind "\"`tput kpp`\": backward-word" (кавычки, экранированные обратной косой чертой, \", передадутся bind в неизменном виде)
Настройка оболочки - это в первую очередь настройка /etc/profile. Это - так называемый sh. Следом выполняется .profile. Этот
Что касается bash, то структура его стартовых файлов сложнее. Прежде всего, ~/.profile выполняется, только если в домашнем каталоге нет файла .bash_profile или .bash_login, иначе стартовый bash выполняет системный и персональный конфигурационные /etc/bashrc и ~/.bashrc. Чтобы стартовый bash также выполнял ~/.bashrc, соответствующую команду необходимо вписать в ~/.bash_profile. Далее, каждый неинтерактивный (запущенный для выполнения bash сверяется с BASH_ENV и, если в этой переменной записано имя существующего файла, выполняет команды оттуда. Наконец, при завершении стартового bash выполняются команды из файла ~/.bash_logout.
Ниже приведены примеры
PS1="\u@\h:\w \$ " EDITOR="/usr/bin/vim" export PS1 EDITOR # Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi
В этом файле вызывается ~/.bashrc (если он существует).
# User specific aliases and functions
if [ -r ~/.alias ]; then
. ~/.alias
fi
# Source global definitions
if [ -r /etc/bashrc ]; then
. /etc/bashrc
fi
Мефодий решил, что сокращения удобнее будет хранить в отдельном файле - ~/.alias. Кроме того, вызывается bashrc, который Мефодий обнаружил в каталоге /etc. Этот файл не входит в число автоматически выполняемых bash, поэтому его выполнение надо задавать явно:
alias > ~/.alias
Заметив, что команда alias выдает список сокращений в том же формате, в котором они и задаются, Мефодий придумал, как обойтись без редактирования файла ~/.alias. Отныне все сокращения, определенные к моменту завершения сеанса работы, будут записываться обратно в .alias. Туда попадут и те сокращения, что были считаны во время выполнения .bashrc, и те, что впоследствии были определены вручную:
alias l='ls -FAC' alias ls='ls --color=auto' alias pd='pushd' alias pp='popd' alias v='ls -ali' alias vi='/usr/bin/vim'
Последняя запись в файле .alias относится к инструменту, с помощью которого Мефодий создавал все эти файлы: текстовому редактору . О текстовых редакторах речь пойдет в следующей лекции.
Некоторое время поработав в Linux и понабирав команды в командной строке, Мефодий пришел к выводу, что в "общении" с оболочкой не помешают кое-какие удобства. Одно из таких удобств - возможность редактировать вводимую строку с помощью клавиши Backspace (удаление последнего символа), " ^W " (удаление слова) и " ^U " (удаление всей строки) - предоставляет сам терминал Linux. Эти команды работают для любого построчного ввода: например, если запустить программу cat без параметров, чтобы та немедленно отображала вводимые с терминала строки. Если по каким-то причинам в строчку на экране попало что-то лишнее, можно нажать " ^R " (redraw) - система выведет в новой строке содержимое входного буфера.
Мефодий не забыл, что cat без параметров следует завершать командой " ^D " (конец ввода). Эту команду, как и предыдущие, интерпретирует при вводе с терминала система. И она же превращает некоторые другие управляющие символы (например, " ^C " или " ^Z ") в stty. Полный список того, что можно настраивать, выдает команда stty -a:
[methody@localhost methody]$ stty -a localhost 38400 baud; rows 30; columns 80; line = 0; intr = ^C; quit = ^\; erase = ^?; kill = ^U; eof = ^D; eol = ; eol2 = ; start = ^Q; stop = ^S; susp = ^Z; rprnt = ^R; werase = ^W; lnext = ^V; flush = ^O; min = 1; time = 0; -parenb -parodd cs8 hupcl -cstopb cread -clocal -crtscts -ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel -iutf8 opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0 isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke
При виде столь обширных возможностей Мефодий немедленно взялся читать руководство ( man stty ), однако нашел в нем не так уж много для себя полезного. Из управляющих символов (строки со второй по четвертую) интересны " ^S " и " ^Q ", с помощью которых можно, соответственно, приостановить и возобновить выдачу на терминал (если текста вывелось уже много, а прочесть его не успеваешь). Можно заметить, что настройка erase (удаление одного символа) соответствует управляющему символу, который возвращается клавишей Backspace именно ^?". На многих терминалах клавиша Backspace возвращает другой символ - " ^H ". Если необходимо переопределить настройку erase, можно воспользоваться командой " stty erase ^H ", причем " ^H " (для удобства) разрешено вводить и как два символа: " ^ " и " H ".
Наконец, чтобы лишить передаваемый символ его управляющих функций (если, например, требуется передать программе на ввод символ с кодом 3, т. е. " ^C "), непосредственно перед вводом этого символа нужно подать команду " ^V " ( lnext ):
[methody@localhost methody]$ cat | hexdump -C Сейчас нажмем Ctrl+C [methody@localhost methody]$ cat | hexdump -C Теперь Ctrl+V, Ctrl+C, enter и Ctrl+D^C 00000000 f4 c5 d0 c5 d2 d8 20 43 74 72 6c 2b 56 2c 20 43 |Теперь Ctrl+V, C| 00000010 74 72 6c 2b 43 2c 20 45 6e 74 65 72 20 c9 20 43 |trl+C, enter и C| 00000020 74 72 6c 2b 44 03 0a |trl+D..| 00000027
Здесь Мефодий прервал, как и собирался, работу первого из cat. При этом до hexdump, cat не успел обработать ни одной строки. Во втором случае " ^C " после " ^V " потеряло управляющий смысл и отобразилось при вводе. С ключом " -C " hexdump выводит также и текстовое представление входного потока, заменяя непечатные символы точками. Так, на точки были заменены и " ^C " (ASCII-код 03 ), и возвращаемый Enter символ конца строки (ASCII-код 0a, в десятичном виде - 10 ). Ни " ^V ", ни " ^D " на вход hexdump, конечно, не попали: их, как управляющие, обработала система.
Прочие настройки stty относятся к обработке текста при выводе на терминал и вводе с него. Они интересны только в том смысле, что при их изменении работать с командной оболочкой становится неудобно. Например, настройка echo определяет, будет ли система отображать на экране все, что вводит пользователь. При включенном echo нажатие любой алфавитно-цифровой клавиши (ввод символа) приводит к тому, что система (устройство типа tty ) выведет этот символ на терминал. Настройка отключается, когда с клавиатуры вводится пароль. При этом трудно отделаться от ощущения, что ввода с клавиатуры не происходит. Еще хуже обстоит дело с настройками, состоящими из кусков вида " i ", " o ", " cr " и " nl ". Эти настройки управляют преобразованием при вводе и выводе исторически сложившегося обозначения конца строки двумя символами в один, принятый в Linux. Может случиться так, что клавиша Enter терминала возвращает как раз неправильный символ конца строки, а преобразование отключено.
Тогда вместо Enter следует использовать " ^J " - символ, на самом деле соответствующий концу строки.
Во всех случаях, когда терминал находится в непонятном состоянии - не реагирует на Enter, не показывает ввода, не удаляет символов, выводит текст "ступеньками" и т. п., рекомендуется "лечить" настройки терминала с помощью stty sane - специальной формы stty, сбрасывающей настройки терминала в некоторое пригодное к работе состояние. Если непонятное состояние терминала возникло однократно, например, после аварийного завершения экранной программы (редактора или оболочки mc ), то можно воспользоваться командой reset. Она заново настраивает терминал в полном соответствии с системной конфигурацией (указанной в файле /etc/, см. лекцию 10)
и ^J stty sane^J ".
Даже не изучая специально возможностей командной оболочки, Мефодий активно использовал некоторые из них, недоступные при вводе текста большинству утилит (в частности, ни cat, ни hexdump ). Речь идет о клавишах Стрелка влево и Стрелка вправо, с помощью которых можно перемещать курсор по командной строке, и клавише Del, удаляющей символ под курсором, а не позади него. В лекции 2 он уже убедился, что эти команды работают в bash, но не работают для cat. Более того, в простом командном интерпретаторе - sh - они тоже не работают.
Следовательно, возможности редактора командной строки специфичны для разных командных оболочек. Однако самые необходимые команды редактирования поддерживаются во всех разновидностях shell сходным образом. По словам Гуревича "во всех видах Linux обязательно есть bash, а если ты достаточно опытен, чтобы устанавливать и настраивать пакеты, можешь установить zsh, у него возможностей больше, чем может понадобиться одному человеку". Поэтому Мефодий занялся изучением документации по bash, что оказалось делом непростым, ибо в bash.info он насчитал более восьми с половиной тысяч строк. Даже про
Попытка "наскоком" узнать все про работу в командной строке принесла некоторую пользу. Во-первых, перемещаться в командной строке можно не только по одному символу вперед и назад, но и по словам: команды escF/escB или Alt+F/Alt+B соответственно (от forward и backward), работают также клавиши home и end, или, что то же самое, " ^A " и " ^E ". А во-вторых, помимо работы с одной командной строкой, существует еще немало других удобств, о которых и пойдет речь в этой лекции.
Двумя другими клавишами со стрелками - вверх и вниз - Мефодий тоже активно пользовался, не подозревая, что задействует этим весьма мощный механизм bash - работу с bash запоминает и позволяет обращаться к ним впоследствии. По стрелке вверх (можно использовать и " ^P ", previous), список поданных команд "прокручивается" от последней к первой, а по стрелке вниз (" ^N ", next) - обратно. Соответствующая команда отображается в командной строке как только что набранная, ее можно отредактировать и подать оболочке (подгонять курсор к концу строки при этом необязательно).
Если необходимо добыть из истории какую-то давнюю команду, проще не гонять список истории стрелками, а выполнить поиск с помощью команды " ^R " (reverse search). При этом выводится подсказка специального вида ("reverse-i-search"), подстрока поиска (окруженная символами ` и ' ) и последняя из команд в истории, в которой эта подстрока присутствует:
[methody@localhost methody]$ ^R | (reverse-i-search)`': i | (reverse-i-search)`i': ls i n | (reverse-i-search)`in': info f | (reverse-i-search)`inf': info o | (reverse-i-search)`info': info ^R | (reverse-i-search)`info': man info ^R | (reverse-i-search)`info': info "(bash.info.bz2)Commands For History"
Пример представляет символы, вводимые Мефодием (в левой части до " | "), и содержимое последней строки терминала. Это "кадры" работы с одной и той же строкой, показывающие, как она меняется при наборе. Набрав "info", Мефодий продолжил поиск этой подстроки, повторяя " ^R " до тех пор, пока не наткнулся на нужную ему команду, содержащую подстроку " info ". Осталось только передать ее bash с помощью Enter.
Чтобы bash записывает ее в файл .bash_history, находящийся в домашнем каталоге пользователя. Делается это в момент завершения оболочки: накопленная за время работы история дописывается в конец этого файла. При следующем запуске bash считывает .bash_history целиком. История хранится не вечно, количество запоминаемых команд в .bash_history ограничено (обычно сохраняется 500 команд, но это можно и перенастроить).
Поиск по истории - удобное средство: длинную командную строку можно не набирать целиком, а отыскать и использовать. Однако давнюю команду придется добывать с помощью нескольких " ^R " - а можно и совсем не доискаться, если она уже выбыла оттуда. Для того чтобы оперативно заменять длинные команды короткими, стоит воспользоваться сокращениями (aliases). В alias без параметров:
[methody@localhost methody]$ alias alias cd..='cd ..' alias cp='cp -i' alias l='ls -lapt' alias ll='ls -laptc' alias ls='ls --color=auto' alias md='mkdir' alias mv='mv -i' alias rd='rmdir' alias rm='rm -i'
С сокращениями Мефодий уже сталкивался в примерах, приведенных в лекции 6, где команда ls отказалась работать в согласии с теорией. Выяснилось, что по команде ls вместо утилиты /bin/ls bash запускает собственную ls --color=auto. Повторно появившуюся в команде подстроку " ls " интерпретатор уже не обрабатывает, во избежание вечного цикла. Например, команда ls -al превращается в результате в ls --color=auto -al. Точно так же любая команда, начинающаяся с rm, превращается в rm -i (interactive), что Мефодия крайне раздражает, потому что ни одно удаление не обходится без вопросов в стиле " rm: удалить обычный файл `файл'?":
[methody@localhost methody]$ unalias cp rm mv [methody@localhost methody]$ alias pd=pushd [methody@localhost methody]$ alias pp=popd [methody@localhost methody]$ pd /bin /bin ~ [methody@localhost bin]$ pd /usr/share/doc /usr/share/doc /bin ~ [methody@localhost doc]$ cd /var/tmp [methody@localhost tmp]$ dirs /var/tmp /bin ~ [methody@localhost tmp]$ pp /bin ~ [methody@localhost bin]$ pp ~ [methody@localhost methody]$ pp -bash: popd: directory stack empty
От надоедливого " -i " Мефодий избавился с помощью команды unalias, а заодно ввел сокращения для полюбившихся ему команд bash - pushd и popd. pushd и popd .cd, меняют текущий каталог. Разница состоит в том, что pushd все каталоги, которые пользователь делает текущими, запоминает в особом списке (стеке). Команда popd удаляет последний элемент этого стека и делает текущим каталогом предпоследний. Обе команды вдобавок выводят содержимое стека каталогов (то же самое делает и команда dirs ). Команда cd в bash также работает со стеком каталогов: она заменяет его последний элемент новым.
Сокращения позволяют быстро набирать команды, однако никак не затрагивают имен файлов, которые чаще всего и оказываются параметрами этих команд. Бывает, что набранной строки - пути к файлу и нескольких первых букв его имени - достаточно для однозначного указания на этот файл, потому что по введенному пути других файлов, чье имя начинается на эти буквы, просто нет. Чтобы не дописывать оставшиеся буквы (а имена файлов в Linux могут быть весьма длинными), Гуревич посоветовал Мефодию нажать клавишу Tab. И - о чудо! - bash сам достроил начало имени файла до целого (снова воспользуемся методом "кадров"):
[methody@localhost methody]$ ls -al /bin/base Tab | [methody@localhost methody]$ ls -al /bin/basename -rwxr-xr-x 1 root root 12520 Июн 3 18:29 /bin/basename [methody@localhost methody]$ base Tab | [methody@localhost methody]$ basename Tab | [methody@localhost methody]$ basename ex Tab | [methody@localhost methody]$ basename examples/ Tab | [methody@localhost methody]$ basename examples/-filename-with- -filename-with-
Дальше - больше. Оказывается, и имя команды можно вводить не целиком: оболочка достроит набираемое слово именно до команды, раз уж это слово стоит в начале командной строки. Таким образом, команду Мефодий набрал за восемь нажатий на клавиатуру (" base " и четыре Tab )! Ему не пришлось вводить начало имени файла в каталоге examples, потому что файл там был всего один.
Выполняя bash может вывести не всю строку, а только ту ее часть, относительно которой у него нет сомнений. Если дальнейшее Tab приведет к тому, что bash растерянно ^G ". Для этого не нужно запускать никаких дополнительных программ: "настоящие" терминалы имеют встроенный динамик, а screen пишет в служебной строке "wuff-wuff" ("гав-гав").Tab.
Поиск ключевого слова "completion" по документации bash выдал так много информации, что Мефодий обратился к Гуревичу за помощью. Однако тот ответил, что не использует bash, и поэтому не в состоянии объяснять тонкости его настройки. Если в bash предусмотрено несколько типов zsh их сколько угодно: существует способ запрограммировать любой алгоритм
mv,
Символы в
abc " соответствует строка abc, но не aBc или ABC, потому что большие и маленькие буквы различаются.* ", соответствует любая строка любой длины (в том числе и пустая).a, + или @, но не ab или 8888.c ").[methody@localhost methody]$ ls .bash* .bash_history .bash_logout .bash_profile .bashrc [methody@localhost methody]$ /bin/e* /bin/ed /bin/egrep /bin/ex [methody@localhost methody]$ ls *a* -filename-with- [methody@localhost methody]$ ls -dF *[ao]* Documents/ examples/ loop to.sort*
Мефодий, как это случается с новичками, немедленно натолкнулся на несколько "подводных камней", неизбежных в ситуации, когда конечный результат неизвестен. Только первая команда сработала не вопреки его ожиданиям: .bash* " был превращен командной оболочкой в список файлов, начинающихся на .bash, этот список получила в качестве параметров командной строки ls, после чего честно его вывела. С " /bin/e* " Мефодию повезло - этот /bin, начинающихся на " e ", и первым файлом в списке оказалась безобидная утилита /bin/echo. Поскольку в командной строке ничего, кроме /bin/echo была воспринята оболочкой /bin/ed, /bin/egrep и /bin/ex.
Что же касается ls *a*, то, по расчетам Мефодия, эта команда должна была выдать список файлов в текущем каталоге, имя которых содержит " a ". Вместо этого на экран вывелось имя файла из подкаталога examples... Впрочем, никакой черной магии тут нет. Во-первых, имена файлов вида " .bash* " хотя и содержат " a ", но начинаются с точки, и, стало быть, считаются скрытыми. *a* " в домашнем каталоге Мефодия bash нашел только подкаталог с именем examples - его-то он и передал в качестве параметра утилите ls. Что было выведено на экран в результате образовавшейся команды ls examples? Конечно, содержимое каталога. *[ao]* " был превращен в список файлов, чьи имена содержат " a " или " o " - Documents examples loop to.sort, а ключ " -d " потребовал от ls показывать информацию о каталогах, а не об их содержимом. В соответствии с ключом " -F ", ls расставила " / " после каталогов и " * " после исполняемых файлов.
Еще одно отличие генерации имен от стандартной обработки / ", разделяющий элементы пути, никогда не ставится в соответствие " * " или диапазону. Происходит это не потому, что искажен алгоритм, а потому, что при генерации имен / ". Например, получить список файлов, которые находятся в каталогах /usr/bin и /usr/sbin и содержат подстроку " ppp " в имени, можно с помощью /usr/*bin/*ppp* ". Однако одного шаблона, который бы включал в этот список еще и каталоги /bin и /sbin - то есть подкаталоги другого уровня вложенности - zsh " предусматривает специальный ** ", которому соответствуют подстройки с любым количеством " / ". Пользоваться им следует крайне осторожно, понимая, что при генерации имен по такому ls, а ls -R или find. Так, использование " /** " в начале
Если перед любым специальным символом стоит " \ ", этот символ лишается специального значения, экранируется: пара " \символ " заменяется командным интерпретатором на " символ " и передается в командную строку без всякой дальнейшей обработки:
[methody@localhost methody]$ echo *o* Documents loop to.sort [methody@localhost methody]$ echo \*o\* *o* [methody@localhost methody]$ echo "*o*" *o* [methody@localhost methody]$ echo *y* *y* [methody@localhost methody]$ ls *y* ls: *y*: No such file or directory
Мефодий заметил, что bash раскрывать не стал, как если бы все " * " в нем были экранированы. В самом деле, какое из двух зол меньше: изменять интерпретацию спецсимволов в зависимости от содержимого каталога, или сохранять логику интерпретации с риском превратить команду с параметрами в команду без параметров? Если бы, допустим, ls *y* превратилась бы в ls и неожиданно выдала бы содержимое всего каталога. Авторы bash (как и Стивен Борн, автор самой первой командной оболочки - sh ) выбрали более непоследовательный,
но и более zsh пошли по другому пути: в этой версии shell использование
Лишить специальные символы их специального значения можно и другим способом. В лекции 2 было рассказано, что
В лекции 5 уже упоминалось, что системный вызов fork(), создавая точную копию родительского процесса, копирует также и
Эти свойства и реализованы в понятии "
[methody@localhost methody]$ date Птн Ноя 5 16:20:16 MSK 2004 [methody@localhost methody]$ LC_TIME=C date Fri Nov 5 16:20:23 MSK 2004
В последнем примере Мефодий воспользовался подсмотренным у Гуревича приемом: присвоил некоторое значение = " внутри первого слова командной строки, приходит к выводу, что это - операция присваивания, а не имя команды, и запоминает, как надо изменить LC_TIME предписывает использовать определенный язык при выводе даты и времени, а значение " C " соответствует стандартному системному языку (чаще всего - английскому).
Если рассматривать shell в качестве высокоуровневого языка программирования, то его переменные - самые обычные строковые переменные. Записать значение в переменную можно с помощью операции присваивания, а прочесть его оттуда - с помощью операции $переменная:
[methody@localhost methody]$ A=dit [methody@localhost methody]$ C=dah [methody@localhost methody]$ echo $A $B $C dit dah [methody@localhost methody]$ B=" " [methody@localhost methody]$ echo $A $B $C dit dah [methody@localhost methody]$ echo "$A $B $C" dit dah [methody@localhost methody]$ echo '$A $B $C' $A $B $C
Как видно из примера, значение неопределенной переменной ( B ) в shell считается пустым и при echo в примере получила, как и первая, два параметра (" " и " dah "), несмотря на то, что переменная B была к тому времени определена и содержала echo получили по одному параметру. Здесь сказалось различие между одинарными и двойными кавычками в shell: внутри двойных кавычек действует
Переменные, которые командный интерпретатор bash определяет после запуска, не принадлежат bash попала в export:
[methody@localhost methody]$ echo "$Qwe -- $LANG" -- ru_RU.KOI8-R [methody@localhost methody]$ Qwe="Rty" LANG=C [methody@localhost methody]$ echo "$Qwe -- $LANG" Rty -- C [methody@localhost methody]$ sh sh-2.05b$ echo "$Qwe -- $LANG" -- C sh-2.05b$ exit [methody@localhost methody]$ echo "$Qwe -- $LANG" Rty -- C [methody@localhost methody]$ export Qwe [methody@localhost methody]$ sh sh-2.05b$ echo "$Qwe -- $LANG" Rty -- C sh-2.05b$ exit
Здесь Мефодий завел новую переменную Qwe и изменил значение LANG, доставшейся стартовому bash от программы login. В результате запущенный sh получил измененное значение LANG и никакой переменной Qwe в export Qwe эта переменная была добавлена в sh.
Во время сеанса работы пользователя login довольно богатое bash можно с помощью команды set. Большинство заранее определенных переменных используются либо самой командной оболочкой, либо утилитами системы, поэтому их изменение приводит к тому, что оболочка или утилиты начинают работать несколько иначе.
Весьма примечательна PATH. В ней содержится список каталогов, элементы которого разделяются двоеточиями. Если команда в командной строке - не собственная команда shell (вроде cd ) и не представлена в виде пути к запускаемому файлу (как /bin/ls или ./script ), то shell будет искать эту команду среди имен запускаемых файлов во всех каталогах PATH, и только в них. Точно так же будут поступать и другие утилиты, использующие или (запуск программы).
По этой причине исполняемые файлы невозможно запускать просто по имени, если они лежат в текущем каталоге, и текущий каталог не входит в PATH. Мефодий в таких случаях пользовался кратчайшим из возможных путей, " ./ " (например, вызывая ./script ):
[methody@localhost methody]$ echo $PATH /home/methody/bin:/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:/usr/games [methody@localhost methody]$ mkdir /home/methody/bin [methody@localhost methody]$ mv to.sort loop script bin/ [methody@localhost methody]$ script Hello, Methody!
Пути, указанные в PATH, могут и не существовать на самом деле. Обнаружив в $PATH элемент /home/methody/bin (подкаталог bin домашнего каталога), Мефодий решил, что гораздо правильнее будет складывать исполняемые файлы не куда попало, а именно в этот каталог: во-первых, это упорядочит структуру домашнего каталога, а во-вторых, позволит запускать эти файлы по имени. Но для начала пришлось такой каталог создать. Порядок, при котором собственные программы лежат в специальном каталоге, куда безопаснее беспорядка, при котором поиск запускаемого файла по имени начинается с текущего каталога. Если в текущем каталоге окажется программа ls и этот каталог - не /bin, а, допустим, /home/shogun/dontrunme, тогда следует ожидать подвоха...
, оперируя независящими от типа терминала понятиями (вроде "очистка экрана" или "позиционирование курсора"), а процедуры из преобразуют их в управляющие последовательности конкретного терминала,
сверившись сначала с $TERM, а затем с содержимым соответствующего раздела базы данных по терминалам, которая называется terminfo .EDITOR (обычно это /usr/bin/vi, о котором речь пойдет в лекции 9). Наконец, некоторые переменные вроде UID, USER или PWD просто содержат полезную информацию, которую можно было бы добыть и другими способами.
Некоторые bash: они задают его свойства и особенности поведения. Таковы переменные семейства PS (Prompt String). В этих переменных хранится строка-подсказка, которую командный интерпретатор выводит в разных состояниях. В частности, содержимое PS1 - это подсказка, которую shell показывает, когда вводит командную строку, а PS2 - когда пользователь нажимает Enter, а интерпретатор по какой-то причине считает, что ввод командной строки не завершен (например, не закрыты кавычки). С $PS2 (символом " > ") Мефодий уже сталкивался в лекциях 2 и 7:
[methody@localhost methody]$ cd examples/ [methody@localhost examples]$ echo $PS1 [\u@\h \W]\$ [methody@localhost examples]$ PS1="--> " --> --> PS1="\t \w " 22:11:47 ~ 22:11:48 ~ 22:11:48 ~ PS1="\u@\h:\w \$ " methody@localhost:~/examples $ methody@localhost:~/examples $ methody@localhost:~/examples $ cd methody@localhost:~ $ methody@localhost:~ $
Мефодий экспериментировал с PS1, параллельно читая документацию по bash (" (bash.info)Printing a Prompt "). Оказывается, в этом командном интерпретаторе содержимое PS1 не просто подставляется при выводе - оно еще и преобразуется, позволяя выводить всякую полезную информацию: имя пользователя (соответствует подстроке " \u ", user ), имя компьютера (" \h ", host ), время (" \t ", time ), путь к текущему каталогу (" \w ", \W " (показывающую последний элемент пути, то есть собственное имя текущего каталога) на " \w ", полный путь, потому что " \w " обладает свойством выделять в полном пути домашний каталог и заменять его на " ~ ". Такое преобразование значений переменных семейства PS1 выполняется, только когда их использует bash в качестве подсказки,
а при обычной
Некогда Мефодий выучил несколько языков программирования, и уже собрался было написать кое-какие нужные программы на Си или Python, однако Гуревич его остановил. Большая часть того, что нужно начинающему пользователю Linux, делается с помощью одной правильной команды, или вызовом нескольких команд в конвейере. От пользователя только требуется оформить решение задачи в виде sh, был настоящим высокоуровневым языком программирования - если, конечно, считать все утилиты системы его операторами. При таком подходе от sh требуется совсем немного: возможность вызывать утилиты, возможность свободно манипулировать результатом их работы и несколько алгоритмических конструкций (условия и циклы).
К сожалению, программирование на shell, а также других, более мощных интерпретируемых языках в Linux, остается за рамками нашего курса. Так что, пока Мефодий читает документацию по bash и упражняется в написании bash - непрактично, так как исполняться они смогут лишь при помощи bash. Если же ограничить себя рамками sh, совместимость с которым объявлена и в bash, и в zsh, и в ash (наиболее близком по возможностям к sh ), и в других командных интерпретаторах, выполняться эти sh- подобных интерпретаторов, и не только в Linux.
Каждый процесс Linux при завершении передает родительскому
[methody@localhost methody]$ grep Methody bin/script echo 'Hello, Methody!' [methody@localhost methody]$ grep -q Methody bin/script ; echo $? 0 [methody@localhost methody]$ grep -q Shogun bin/script ; echo $? 1 [methody@localhost methody]$ if grep -q Shogun bin/script ; then echo "Yes"; fi [methody@localhost methody]$ if grep -q Methody bin/script ; then echo "Yes"; fi Yes
if запускает команду- условие, grep -q, которая ничего не выводит на экран, зато возвращает 0, если 1, если нет. В зависимости от if выполняет или не выполняет тело: список команд, заключенный между then и fi. Точка с запятой разделяет операторы в sh ; либо она, либо перевод строки необходимы перед then и fi, иначе все, что идет после grep, интерпретатор посчитает параметрами этой утилиты.
Множеством функций обладает команда test: она умеет сравнивать числа и строки, проверять ярлык объекта файловой системы и наличие самого этого объекта. У " test " есть второе имя: " [ " (как правило, /usr/bin/[ - символьная или даже /usr/bin/test), позволяющее оформлять оператор if более привычным образом:
[methody@localhost methody]$ if test -f examples ; then ls -ld examples ; fi [methody@localhost methody]$ if [ -d examples ] ; then ls -ld examples ; fi drwxr-xr-x 2 methody methody 4096 Окт 31 15:26 examples [methody@localhost methody]$ A=8; B=6; if [ $A -lt $B ] ; then echo "$A<$B" ; fi [methody@localhost methody]$ A=5; B=6; if [ $A -lt $B ] ; then echo "$A<$B" ; fi 5<6
Команда test -f проверяет, не является ли ее аргумент файлом; поскольку examples - это каталог, результат будет неудачным. Команда [ -d - то же самое, что и test -d (не каталог ли первый параметр), только последним параметром этой команды - исключительно для красоты - должен быть символ " ] ". Результат - успешный, и выполняется команда ls -ld. Команда test параметр1 -lt параметр3 проверяет, является ли параметр1 числом, меньшим, чем (less than) параметр3. В более сложных случаях оператор if удобнее записывать "лесенкой", выделяя переводами строки окончание условия и команды внутри тела (их может быть много).
Второй тип `команда` " (другой вариант - " $(команда) "). Как и
[methody@localhost methody]$ A=8; B=6 [methody@localhost methody]$ expr $A + $B 14 [methody@localhost methody]$ echo "$A + $B = `expr $A + $B`" 8 + 6 = 14 [methody@localhost methody]$ A=3.1415; B=2.718 [methody@localhost methody]$ echo "$A + $B = `expr $A + $B`" expr: нечисловой аргумент 3.1415 + 2.718 = [methody@localhost methody]$ echo "$A + $B" | bc 5.8595 [methody@localhost methody]$ C=`echo "$A + $B" | bc` [methody@localhost methody]$ echo "$A + $B = $C" 3.1415 + 2.718 = 5.8595
Сначала для арифметических вычислений Мефодий хотел воспользоваться командой expr, которая работает с параметрами командной строки. С целыми числами expr работает неплохо, и ее результат можно подставить прямо в аргумент команды echo. С действительными числами умеет работать утилита- bc ; арифметическое выражение пришлось сформировать с помощью echo и передать по конвейеру, а результат поместить в переменную C. Во многих современных командных оболочках есть встроенная целочисленная арифметика вида " $(( выражение )) ".
В языке sh много внимания было уделено удобству написания
[methody@localhost methody]$ cat > bin/two #!/bin/sh echo "Running $0: $*" $1 $3 $2 $3 [methody@localhost methody]$ chmod +x bin/two [methody@localhost methody]$ bin/two file "ls -ld" examples Running bin/two: file ls -ld examples examples: directory drwxr-xr-x 2 methody methody 4096 Окт 31 15:26 examples [methody@localhost methody]$ two "ls -s" wc "bin/two bin/loop" junk Running /home/methody/bin/two: ls -s wc bin/two bin/loop junk 4 bin/loop 4 bin/two 4 9 44 bin/two 1 5 26 bin/loop 5 14 70 итого
Как видно из примера, форма " $номер_параметра " позволяет обратиться и к нулевому параметру - команде, а вся строка параметров хранится в переменной " * ". Кроме того, свойство ls -ld " или " bin/two bin/loop ", а интерпретатору - разбить эти параметры на имя команды и ключи и два имени файла соответственно.
В sh есть и while, формат которого аналогичен if, и более удобный именно в for (список делится на слова так же, как и командная строка - с помощью
[methody@localhost methody]$ for Var in Wuff-Wuff Miaou-Miaou; do echo $Var; done Wuff-Wuff Miaou-Miaou [methody@localhost methody]$ for F in $(date); do echo -n "<$F>"; done; echo <Сбт><Ноя><6><12:08:38><2004> [methody@localhost methody]$ cat > /tmp/setvar QWERTY="$1" [methody@localhost methody]$ sh /tmp/setvar 1 2 3; echo $QWERTY [methody@localhost methody]$ . /tmp/setvar 1 2 3; echo $QWERTY 1
Во втором for Мефодий воспользовался date, каждое слово которой вывел с помощью echo -n в одну строку, а в конце команды пришлось вывести один перевод строки вручную.
Вторая половина примера иллюстрирует ситуацию, с которой Мефодий столкнулся во время своих экспериментов: все переменные, определяемые в exit, exec или какая-нибудь другая, приводящая к завершению работы интерпретатора, завершится именно текущая командная оболочка, чем сеанс работы пользователя в системе может и закончиться.
Научившись (главным образом в результате чтения документации и непрестанных экспериментов) создавать работающие
Оказалось, что настройка управляющих клавиш в bash не выглядит как bash, а ко всем программам, использующим readline. readline называется .inputrc и состоит, в основном, из команд вида " управляющая_последовательность ": функция, где управляющая_последовательность - это символы, при получении которых readline выполнит функцию работы с вводимой строкой. Список всех функций readline можно узнать у bash по команде bind -l, а список всех привязок этих функций к клавиатурным последовательностям - по команде bind -p. Мефодий вписал в .inputrc такие две строки:
"\e[5~": backward-word "\e[6~": forward-word
Упомянутые в примере функции позволяют перемещать курсор в командной строке по словам, а esc-последовательности возвращаются, соответственно, клавишами Page Up и Page Down \e " означает в .inputrc клавишу esc, то есть " ^[ ", символ с ASCII-кодом 27 ).
К одной и той же функции readline можно привязать сколько угодно управляющих последовательностей: например, клавиша home делает то же, что и " ^A ", Стрелка вверх - то же, что и " ^P ", а Del - то же, что и " ^D " (только не в пустой строке!). Таким образом отчасти решается проблема несовместимости управляющих последовательностей терминалов: если в каком-нибудь терминале другого типа Page Up или Page Down будут возвращать другие последовательности, Мефодий просто добавит в .inputrc еще одну пару команд. Правда, Гуревич советовал ему вовсе отказаться от редактирования .inputrc, а воспользоваться утилитой tput, которая обращается к переменной TERM и базе данных по терминалам terminfo и готова выдать верную для любого данного терминала информацию по kpp (key previous page) и knp
(key next page).
Выдачу tput можно "скормить" той же bind, и получить команду, которая работает на любом терминале: bind "\"`tput kpp`\": backward-word" (кавычки, экранированные обратной косой чертой, \", передадутся bind в неизменном виде)
Настройка оболочки - это в первую очередь настройка /etc/profile. Это - так называемый sh. Следом выполняется .profile. Этот
Что касается bash, то структура его стартовых файлов сложнее. Прежде всего, ~/.profile выполняется, только если в домашнем каталоге нет файла .bash_profile или .bash_login, иначе стартовый bash выполняет системный и персональный конфигурационные /etc/bashrc и ~/.bashrc. Чтобы стартовый bash также выполнял ~/.bashrc, соответствующую команду необходимо вписать в ~/.bash_profile. Далее, каждый неинтерактивный (запущенный для выполнения bash сверяется с BASH_ENV и, если в этой переменной записано имя существующего файла, выполняет команды оттуда. Наконец, при завершении стартового bash выполняются команды из файла ~/.bash_logout.
Ниже приведены примеры
PS1="\u@\h:\w \$ " EDITOR="/usr/bin/vim" export PS1 EDITOR # Get the aliases and functions if [ -f ~/.bashrc ]; then . ~/.bashrc fi
В этом файле вызывается ~/.bashrc (если он существует).
# User specific aliases and functions
if [ -r ~/.alias ]; then
. ~/.alias
fi
# Source global definitions
if [ -r /etc/bashrc ]; then
. /etc/bashrc
fi
Мефодий решил, что сокращения удобнее будет хранить в отдельном файле - ~/.alias. Кроме того, вызывается bashrc, который Мефодий обнаружил в каталоге /etc. Этот файл не входит в число автоматически выполняемых bash, поэтому его выполнение надо задавать явно:
alias > ~/.alias
Заметив, что команда alias выдает список сокращений в том же формате, в котором они и задаются, Мефодий придумал, как обойтись без редактирования файла ~/.alias. Отныне все сокращения, определенные к моменту завершения сеанса работы, будут записываться обратно в .alias. Туда попадут и те сокращения, что были считаны во время выполнения .bashrc, и те, что впоследствии были определены вручную:
alias l='ls -FAC' alias ls='ls --color=auto' alias pd='pushd' alias pp='popd' alias v='ls -ali' alias vi='/usr/bin/vim'
Последняя запись в файле .alias относится к инструменту, с помощью которого Мефодий создавал все эти файлы: текстовому редактору . О текстовых редакторах речь пойдет в следующей лекции.
Для получения официальных документов о завершении программы дополнительного профессионального образования (удостоверения о повышении квалификации, дипломов о профессиональной переподготовке и MBA) необходимо предоставить:
Внимание! Вы можете не заказывать доставку бумажной версии официального документы, а скачать его в электронном виде и распечатать самостоятельно. Информация о выданном документе в течение 1 месяца загружается в Федеральную информационную систему «Федеральный реестр сведений о документах об образовании и (или) о квалификации, документах об обучении» - ФИС ФРДО.
Доступ на новый сайт осуществляется с использованием адреса электронной почты, который был указан вами при регистрации на "старом". Мы постарались перенести все ваши данные с прежнего ресурса, однако не исключена вероятность потери части информации.
При возникновении проблемы со входом, воспользуйтесь функцией сброса пароля
Если вы обнаружите несоответствия, пожалуйста, сообщите нам.