| ||||||||||||||||
| ||||||||||||||||
| ||||||||||||||||
Кто командует парадом? В двух предыдущих статьях* мы рассмотрели файловую систему и управление процессами в ОС Linux. На все это мы глядели со стороны ядра: строго говоря, термин «Linux» и относится к ядру, а ОС в целом правильнее именовать GNU/Linux, поскольку многие критически важные ее компоненты взяты из системы GNU, создаваемой Фондом свободного ПО (Free Software Foundation, FSF). Именно так, кстати, делается в официальных названиях многих дистрибутивов (например, Debian GNU/Linux). Настало время вспомнить и о пользователе — ведь это для него, собственно, создаются файлы и запускаются процессы, — а также о том, что для управления системой ему необходим пользовательский интерфейс. В этой статье речь пойдет о традиционном для UNIX-систем интерфейсе командной строки и о языке наиболее популярного в Linux командного интерпретатора bash. Вы, возможно, удивитесь: есть же графические оболочки, такие как GNOME или KDE; именно благодаря им наблюдается сейчас бурный рост популярности Linux! Разве с их появлением командная строка не превратилась в никому не нужный анахронизм? Вовсе нет. Она по-прежнему остается самым удобным средством комбинирования программ и автоматизации рутинных процедур. Что касается графических оболочек, то они одновременно и слишком просты, и слишком сложны. Элементарные действия вроде запуска отдельных программ и основных операций с файлами выполняются там очень похоже на то, как это происходит в Windows, и пользователь, знакомый с Windows, легко освоит их без посторонней помощи. Организация же взаимодействия программ, наоборот, требует довольно высокой программистской квалификации: например, среда GNOME основана на модели CORBA, а манипулировать CORBA-объектами весьма непросто. Командный интерпретатор предоставляет в наше распоряжение некую «золотую середину» — возможности весьма широкие и при этом относительно легко осваиваемые. Примеры и иллюстрации, как и в предыдущих статьях, приводятся на материале дистрибутива KSI-Linux Release 3.0 (Manticore). Скрипты и интерпретаторы Командный интерпретатор, как следует из его названия, интерпретирует команды, т. е. выполняет их непосредственно (без предварительной компиляции). Он обрабатывает команды, вводимые пользователем в командной строке, а также скрипты — заранее подготовленные последовательности команд, хранящиеся в текстовом виде. Надо сказать, что скрипты играют в GNU/Linux (и UNIX вообще) куда более важную роль, чем командные файлы в Windows и DOS. Например, из более чем тысячи (!) программ в каталоге /usr/bin того компьютера, на котором пишутся эти строки, примерно четверть является скриптами того или иного вида, а уж количество вспомогательных скриптов, используемых разными программами для внутренних нужд и не предназначенных для исполнения «широкой публикой» (а потому хранящихся в других каталогах), вообще не поддается учету. На плечи скриптов ложится и большая часть «тяжелой работы» по запуску системы. А если требуется автоматизировать какие-либо действия, то самый простой способ — опять-таки написать несложный скрипт. В любой «полноценной» (не сокращенной для помещения в тостер или мобильный телефон) версии GNU/Linux имеется не менее двух командных интерпретаторов плюс еще три-четыре языка скриптов, не используемых в командной строке (таких как perl, tcl, python или scheme), и это не считая «мини-языков» типа sed или awk. Почему бы не ограничиться одним интерпретатором и его командным языком? Главным образом потому, что люди не похожи друг на друга и у них разные предпочтения. И чтобы учесть интересы максимального числа пользователей, создатели дистрибутивов включают в них по несколько интерпретаторов, а администраторы обычно предоставляют пользователям своих систем право выбрать по собственному вкусу язык для работы в командной строке. Из всех командных интерпретаторов для UNIX-систем два являются «классическими». Это B Shell (Bourne Shell), созданный Стефеном Бурном (Stephen R. Bourne) для седьмой версии UNIX, и C Shell, разработанный в Беркли Уильямом Джоем (William N. Joy). Язык C Shell, основанный на командном интерпретаторе шестой версии UNIX, содержал ряд расширений, помогающих в интерактивной работе и в написании скриптов: историю команд, псевдонимы (aliases), массивы и многое другое. Однако при всех своих несомненных преимуществах он имел один очень серьезный недостаток — был несовместим с B Shell. Поэтому, когда FSF разработал интерпретатор bash (Bourne-Again SHell), сочетающий синтаксис B Shell с мощью C Shell, привлекательность C Shell значительно снизилась. И хотя многие бывшие пользователи BSD или коммерческих версий UNIX используют C Shell при работе в GNU/Linux, стандартом де-факто в этой ОС является bash. (Впрочем, для аварийных дискет bash, занимающий «целых» 420 Кбайт, великоват, и на них часто помещают более компактный интерпретатор, например A Shell, вмещающийся в 62 Кбайт.) Именно bash интерпретирует основную массу скриптов из /usr/bin и подавляющее большинство вспомогательных скриптов. (Поскольку B Shell не может быть включен в GNU/Linux по лицензионным соображениям, скрипты, изначально рассчитанные на B Shell, также интерпретируются посредством bash.) Поэтому из всех командных интерпретаторов пользователю GNU/Linux в первую очередь необходимо освоить bash. Подробно описывать bash в журнальной статье невозможно, да, впрочем, и не слишком нужно: в конце концов, он снабжен весьма подробной документацией, которая вызывается командой info bash. Здесь же мы остановимся на наиболее характерных и интересных его особенностях. Команды и метасимволы Конечно, bash может выполнять любые команды, имеющиеся в системе, но некоторые из них являются подпрограммами интерпретатора (внутренние команды), а некоторые другие, хотя и представляют собой самостоятельные программы, специально предназначены для использования в командных скриптах (внешние команды). Внутренних и внешних команд bash насчитывается более сотни; перечень наиболее, на мой взгляд, часто применяемых с краткими описаниями приводится во врезке на с. 154. (Оценка употребительности, разумеется, чисто субъективная: я, например, обычно получаю имя файла без пути с помощью конструкции ${filename//*\/}, а кто-то, возможно, использует для этого специальную команду basename, хотя она внешняя и из-за этого работает несколько медленнее.) Как же устроена сама команда? Ее базовая структура во всех языках скриптов одинакова и весьма проста: сначала записывается имя команды, за ним может следовать определенное число опций (ключей) и аргументов (параметров), отделяемых от имени и друг от друга пробелами. Регистр символов существенен в любом месте команды; имена большинства команд записываются строчными буквами. Специальные символы — ‘*’, ‘$’, ‘?’ ‘!’, ‘>’, ‘<’ и др. — используются в командах в качестве метасимволов, т. е. служат для управления работой самого интерпретатора; именно поэтому их избегают употреблять в именах команд, файлов, каталогов и т. д. При необходимости вывести на экран строку, содержащую спецсимволы, ее обычно заключают в кавычки (существуют и другие способы вывода спецсимволов, но этот — самый распространенный). Стандартно каждая команда записывается на отдельной строке, но можно поместить в одной строке и несколько команд: они отделяются друг от друга точками с запятой, если нужно, чтобы очередная команда ждала завершения работы предыдущей, и амперсандами, если команды должны выполняться параллельно. Длинную команду, которой не хватает одной строки, можно перенести на следующую с помощью символа ‘\’. Радости интерактивной работы Надо сказать, что для интерактивной работы bash предоставляет массу удобств (на которые вы уже, скорее всего, обращали внимание, если работали в GNU/Linux). Он поддерживает такие средства редактирования командной строки, как повтор символов, макросы, «карман» (буфер) и т. д., а также историю (т. е. возможность повторить ранее введенную команду) и настраиваемое автоматическое дополнение. Так, чтобы запустить, скажем, программу mysql_ convert_table_format, достаточно набрать в командной строке mysql_co и нажать клавишу табуляции: bash, зная названия доступных команд, сам «допишет» имя. (Если в системе есть несколько команд, начинающихся с заданного префикса, он выдаст их перечень, а если их более 100, то предварительно уточнит, действительно ли нужен такой огромный список. Кстати, с помощью данного свойства bash легко выяснить число доступных команд: для этого достаточно нажать клавишу табуляции, находясь в начале строки.) А когда название команды введено (и после него поставлен пробел), интерпретатор позволяет тем же способом ввести имя файла. Автозаполнение также можно вызвать, нажав клавишу <Meta> (в ее роли обычно выступает <Alt>) одновременно с одним из специальных символов: ‘/’ вызывает дополнение имени файла, ‘!’ — команды, $ — переменной, ‘~’ — пользователя, @ — машины. А при нажатии последовательно клавиш <Ctrl>+x и соответствующего специального символа выдается список возможных вариантов дополнения. Но и это еще не все. Например, автодополнение можно программировать... Вернемся, однако, к языку интерпретатора bash. Шаблоны Поскольку ряд идей B Shell был использован при создании командных интерпретаторов DOS и Windows NT (Windows 9X не имеет собственного интерпретатора), многие конструкции bash могут показаться вам знакомыми. Однако это сходство зачастую обманчиво и нередко вводит в заблуждение пользователя DOS/Windows. Хорошей иллюстрацией здесь могут послужить шаблоны. Базовые правила задания шаблонов для имен файлов в bash довольно просты: ‘*’, как и в DOS, означает любое число любых символов, а ‘?’ — любой одиночный символ. Кроме того, можно перечислить символы в квадратных скобках (разрешается вставлять между ними пробелы); а символ ‘^’ или ‘!’ в начале такого списка будет указывать, что символы не должны встречаться в данной позиции. Если интерпретатор DOS поручает обработку шаблонов конкретным командам, то bash выполняет ее самостоятельно и передает командам не шаблоны, а готовые списки подходящих файлов. В результате команды иногда ведут себя совсем не так, как ожидает пользователь DOS.
Посмотрите на рис. 1: исходя из опыта работы в DOS естественно было бы предположить, что каждый из файлов с именем вида ‘*.static’ будет скопирован в файл с таким же именем, но без расширения, а образовался файл с диким именем ‘*.’ (впрочем, это произошло только потому, что в каталоге имелся всего один файл, подходивший под шаблон; если бы их оказалось несколько, bash выдал бы сообщение об ошибке). Как же тогда получить имена без расширений (точнее, с отсеченной частью после точки — в Linux нет расширений в понимании DOS)? Об этом вы узнаете в конце следующего раздела, посвященного переменным. Переменные Начнем с отличий между переменными bash и командного языка DOS. Их удобно продемонстрировать на примере. Рассмотрим команду, которая «дописывает» в переменную окружения CLASSPATH путь к архиву JAVApackage (подобные команды часто вставляются в конфигурационные файлы инсталляторами различных программ). В DOS она имела бы приблизительно следующий вид: set CLASSPATH=%CLASSPATH%;c:\Program Files\Big Program\ JAVApackage.jar А в «версии для Linux» она может выглядеть так: CLASSPATH=”$CLASSPATH${CLASSPATH:+:} /opt/big-program/ JAVApackage.zip” export CLASSPATH или так: export CLASSPATH=”$CLASSPATH${CLASSPATH:+:}/opt/ big-program/JAVApackage.zip” Как видим, в DOS переменные ограничиваются с двух сторон символами ‘%’, а в bash маркируется только их начало — символом ‘$’: признаком конца служит первый символ, не разрешенный в именах переменных (разрешены буквы, цифры и символ подчеркивания). Кроме того, в bash нет команды, аналогичной SET: интерпретатор распознает присваивание значения переменной просто по наличию знака равенства. Кстати, благодаря тому, что установка переменной окружения в bash не требует отдельной команды, ее можно сделать частью любой команды (рис. 2). Присвоенное таким образом значение действительно только для данной команды, а в среду bash изменения не вносятся.
Вернемся, однако, к нашему примеру. Несмотря на более короткую форму записи самих переменных и операции присваивания, «Linux-версия» оказалась длиннее. Во-первых, там, где командный язык DOS обходится одним-единственным знаком ‘;’, она содержит устрашающего вида конструкцию ${CLASSPATH:+:}, а во-вторых, в ней присутствует несколько загадочная для пользователя DOS команда export. И то, и другое — следствие заботы о безопасности. Команда export делает переменную доступной другим командам. В командных файлах DOS переменные всегда внешние, и в подавляющем большинстве случаев это действительно нужно, поскольку переменные служат почти исключительно для обмена данными между программами. В bash же широко используются внутренние переменные — в качестве счетчиков, для хранения промежуточных результатов вычислений или имен файлов и т. д. Поэтому переменные, которые должны быть доступны за пределами данного скрипта, специальным образом отмечаются. Конечно, маловероятно, что имя внутренней переменной случайно совпадет с именем переменной окружения, в результате чего значение последней окажется испорченным и какая-то программа начнет работать неправильно, но, как говорил Козьма Прутков, «лучше перебдеть, чем недобдеть». Конструкция ${CLASSPATH:+:} вставляет в строку двоеточие (которое в bash служит разделителем элементов CLASSPATH, а также PATH, играющей ту же роль, что и в DOS), но лишь при условии, что строка CLASSPATH не является пустой. Без этой меры предосторожности результатом выполнения команды могла бы оказаться переменная CLASSPATH вида :/opt/big-program /JAVApackage. zip, т. е. с пустым элементом в начале. Такой элемент обозначает текущий каталог, который в Linux, в отличие от DOS, необходимо включать в CLASSPATH (и в PATH) в явной форме. Причем в большинстве дистрибутивов это не делается — из тех же соображений «как бы чего не вышло». Данное обстоятельство часто сбивает с толку начинающих пользователей Linux: — Как же так: я ведь и атрибуты executable for all на файл MyGreatCommand поставил, и в первой строке #!/bin/sh написал, а мне все равно говорят: command not found! — Конечно! Ведь ты же ее в PATH не поместил! — Какой PATH? Она у меня в текущем каталоге! — А у тебя разве текущий каталог входит в PATH? — ??? Впрочем, путь к команде, находящейся в текущем каталоге, можно указать в форме ./MyGreatCommand, и этого будет достаточно, чтобы она запустилась. Если в DOS с переменной можно сделать, грубо говоря, две вещи — присвоить ей значение и извлечь значение, присвоенное ранее, — то в bash вариантов намного больше: скажем, извлечь значение можно десятком разных способов, включая условное извлечение (с которым мы познакомились на примере конструкции ${CLASSPATH:+:}), извлечение подстроки и извлечение с использованием шаблона. В частности, конструкция ${X##шаблон} позволяет, извлекая строку, удалить из нее максимально возможную соответствующую шаблону подстроку, считая от начала, а ${X%шаблон} — минимально возможную, считая с конца. Так что отсечь «хвосты» упомянутым в предыдущем разделе именам файлов можно было бы, например, следующим образом: for i in /bin/*.static do j=${i##*/} cp ”$i” ”${j%\.static}” done Подробнее о разных вариантах извлечения переменных рассказывается во врезке на с. 156. А операторами for и do мы займемся во второй части статьи. Системные переменные Помимо переменных, используемых различными программами, в Linux, как и в DOS, есть специальные, или «системные» переменные, значение которых определено заранее, причем их намного больше. Так, DOS имеет переменную PROMPT, содержащую приглашение командной строки, а в bash ей соответствуют четыре переменных: PS1 — основное приглашение; PS2 — «вспомогательное» приглашение, выдаваемое, когда команда не уместилась на одной строке; PS3 — приглашение «команды» select (на самом деле это не команда, а специальная конструкция bash, призванная облегчить выбор из нескольких вариантов; впрочем, она используется довольно редко); PS4 — приглашение перед командами скрипта, выводимыми в режиме трассировки (заметим, что в bash, в отличие от командного интерпретатора DOS, скрипты по умолчанию не трассируются). В документации bash описано множество переменных, устанавливаемых интерпретатором или влияющих на его поведение. Назовем для примера $RANDOM, дающую доступ к простому генератору псевдослучайных чисел, и $!, значение которой равно PID последней команды, запущенной из данного экземпляра интерпретатора на асинхронное выполнение. Нам уже встречались системные переменные ${CLASSPATH, $PATH, а также $LC_ALL, определяющая страну и язык. С другими, такими как $? — возвращаемое значение — или $* — список параметров, — мы познакомимся в дальнейшем. Окончание в следующем номере. * В. Хименко. «Файлы, файлы, файлы». «Мир ПК», № 2/2000, с. 64; № 3/2000, с. 50. «Процессы, задачи, потоки и нити». «Мир ПК», № 5/2000, с. 42; № 6/2000, с. 54. Команды bash Перед тем как перечислять в алфавитном порядке наиболее употребительные команды bash, необходимо назвать три «справочных» команды, используемых почти исключительно при интерактивной работе, — help, info и man. Команда help выдает краткое описание любой встроенной команды bash, info предоставляет доступ к входящему в состав системы GNU развернутому справочнику, в котором bash, разумеется, подробно описан. Команда man позволяет обратиться к другому, более старому справочнику, где есть информация о ряде команд, не описанных в справочнике info (вообще говоря, команда info, не найдя сведений в собственном справочнике, ищет их и в справочнике man, однако кое-что можно получить только с помощью самой man). Естественно, команда help help позволяет получить справку по help, команда info info — по info, а man man — по man. В приводимом ниже списке каждая команда снабжена пометкой, указывающей, как получить ее более подробное описание: (b) означает, что команда встроенная и, следовательно, информацию о ней предоставляет команда help, (i) соответствует команде info, (m) — команде man. . (b) — синоним для команды source : (b) — синоним для команды true [ (b) — сокращение для команды test, но, в отличие от нее, требует закрывающей квадратной скобки (( (b) — соотносится с командой let так же, как [ соотносится с test [[ (b) — не вполне команда, а особое выражение, очень похожее на команду [ (test) alias(b) — позволяет задавать псевдонимы для других команд at(m) — ставит программу в очередь на выполнение в заданное время atq(m) — в заданное время проверяет очередь программ на выполнение atrm(m) — в заданное время удаляет программу из очереди на выполнение awk(i) — язык для построчного сканирования и обработки файлов: простой, маленький и быстрый, но притом достаточно мощный batch(m) — выполняет программу, когда система не слишком загружена builtin(b) — позволяет вызвать встроенную команду bash, даже когда ее имя перекрыто именем функции или псевдонимом bzip2(i) — более новая, чем gzip, программа сжатия файлов; работает медленнее, чем gzip, но обеспечивает лучший коэффициент сжатия cat(i) — «склеивает» заданные файлы и выдает их на стандартный выход cd(b) — изменяет текущий каталог chgrp(i), chmod(i), chown(i) — изменяют соответственно группу, права доступа и владельца файла command(b) — позволяет вызвать команду — встроенную или внешнюю, даже когда ее имя перекрыто именем функции или псевдонимом cp(i) — копирует файлы и каталоги cpio(i) — CoPy In/Out — системная программа создания архивов; не содержит встроенной поддержки сжатия файлов, но может использоваться совместно с gzip или bzip2 crontab(m) — позволяет модифицировать список регулярных заданий пользователя cut(i) — выдает на стандартный выход выбранные части строк текстового файла dd(i) — копирует файл блоками, выполняя одновременно некоторые дополнительные действия du(i) — вычисляет объем, занятый на диске указанными файлами declare(b) — позволяет задать имя и тип переменной (применяется не слишком часто, так как bash допускает использование необъявленных переменных) df(i) — сообщает количество свободного и занятого места на диске diff(i) — находит различия между двумя файлами dirs(b) — выводит список запомненных подкаталогов echo(b/i) — выводит на стандартный выход заданное сообщение enable(b) — позволяет разрешить или запретить использование встроенных команд eval(b) — выполняет аргументы так, как если бы они были введены в командной строке (ранее часто использовалась для обращения к переменной, имя которой содержится в другой переменной) exec(b) — выполняет системный вызов exec, т. е. замещает процесс, где исполняется скрипт, другим, заданным в качестве параметра; часто используется в так называемых «скриптах-обертках» (wrapper scripts), настраивающих среду для выполнения программ exit(b) — завершает работу командного интерпретатора (и, стало быть, скрипта) export(b) — делает переменные данного скрипта доступными для других процессов, запущенных из командного интерпретатора file(m) — определяет тип файла (по содержимому; эвристический анализ выполняется на основе гибкой настраиваемой базы данных) find(m/i) — ищет файлы по множеству признаков, но не по содержимому false(b/i) — возвращает код ненормального завершения getopts(b) — довольно сложная команда, похожая на аналогичное средство системной библиотеки; позволяет создавать скрипты, понимающие сложные опции grep(m) — ищет строки в файлах; может использоваться совместно с командами find и xargs для поиска файлов по содержимому gzip(i) — стандартная для GNU программа сжатия файлов; способна распаковывать (но не создавать) файлы в формате compress — более старой UNIX-программы сжатия install(i) — копирует файлы, одновременно позволяя устанавливать их атрибуты kill(b/m) — позволяет послать процессу сигнал; по умолчанию посылается сигнал SIGTERM, останавливающий процесс; отсюда такое устрашающее название less(m) — улучшенная по сравнению с more программа просмотра файлов let(b) — вычисляет арифметическое выражение; выражение может содержать многие операторы языка Си, а также переменные local(b) — создает локальную (внутри функции) переменную logout(b) — завершает работу командного интерпретатора, являющегося основным (login shell) ln(i) — создает ссылки на файлы (дополнительные жесткие или символические) ls(i) — выводит список файлов (например, для заданного каталога) md5sum(i) — подсчитывает для файлов 128-битовую контрольную сумму mkdir(i) — создает подкаталог mktemp(m) — создает временный файл (чтобы избежать «дыр» в безопасности, создавайте временные файлы только с помощью mktemp) more(m) — постранично выводит файл на экран (служит для просмотра длинных файлов) mv(i) — перемещает или переименовывает файлы (каталоги) patch(i) — применяет diff-файл (см. diff) к исходному файлу popd(b) — удаляет подкаталоги из списка запомненных подкаталогов printf(b/i) — обеспечивает форматированную печать данных (имеет много общего с одноименной функцией стандартной библиотеки Си) pushd(b) — добавляет подкаталог в список запомненных подкаталогов и перемещает подкаталоги внутри этого списка pwd(b/i) — выводит путь к текущему каталогу read(b) — считывает строку со стандартного ввода и присваивает прочитанные значения указанным переменным readonly(b) — защищает переменную от случайных изменений return(b) — выходит из функции и передает управление в вызвавшую программу rm(i) — удаляет файлы (подкаталоги) rmdir(i) — удаляет пустые подкаталоги sed(i) — потоковый редактор (a Stream EDitor); дает возможность быстро производить с текстом простые операции (например, поиск с заменой) select(b) — довольно сложная команда, позволяющая организовывать меню с выбором вариантов из списка (в действительности это даже не команда, а особая синтаксическая форма, родственная синтаксическим формам while и for) set(b/i) — очень сложная команда:
(Команда help set не дает полного описания set; оно есть только в описании bash, получаемом командой info bash.) shift(b) — сдвигает позиционные параметры ($1 становится равным $N, $2 — $N+1, $3 — $N+2 и т.д.) sort(i) — сортирует файл source(b) — читает и выполняет команды, содержащиеся в файле (часто используется для того, чтобы вынести определение переменных в отдельный файл конфигурации) tar(i) — программа создания архивов (Tape ARchiver); не содержит встроенной поддержки сжатия файлов, но может использоваться совместно с gzip или bzip2 test(b/i) — вычисляет значение логического выражения; в основном проверяет атрибуты файлов (существует? пуст? исполняемый? подкаталог? и т. д.), однако может также сравнивать строки tr(i) — заменяет одни символы на другие по заданной таблице подстановки trap(b) — позволяет связать с сигналом особую обработку true(b/i) — возвращает код успешного завершения type(b) — возвращает «тип» слова, заданного в качестве аргумента (встроенная команда, псевдоним, функция и т. д.) ulimit(b) — устанавливает или сообщает системные квоты для процесса (процессов) umask(b) — назначение описано в статье «Файлы, файлы, файлы» unalias(b) — удаляет имя из списка псевдонимов uniq(i) — выводит уникальные (или, наоборот, повторяющиеся) строки в отсортированном файле unset(b) — удаляет имя из списка переменных wc(i) — подсчитывает число символов, слов и строк в файле xargs(m) — получает параметры со стандартного входа и вызывает с этими параметрами заданную программу (по умолчанию echo) Как уже говорилось, здесь перечислены далеко не все команды. В типичной системе GNU/Linux их значительно больше: есть, например, команды, выводящие восьмеричный и шестнадцатеричный дамп памяти (od и hexdump), печатающие начало и конец файла (head и tail), а ко многим упомянутым командам есть дополнительные (например, diff3 позволяет сравнить три файла, а bzcat — просмотреть файл, упакованный программой bzip2). Не попали в наш обзор и системные переменные, имеющие для bash особый смысл. Обо всем этом и о многом другом вы сможете узнать, набрав в командной строке слова info bash. Извлечение значений переменных [khim@localhost tmp]$ VAR1=1234567890 [khim@localhost tmp]$ VAR2=0987654321 [khim@localhost tmp]$ echo "$VAR1 $VAR2 XXX${VAR1}XXX ZZZ${VAR2}ZZZ" 1234567890 0987654321 XXX1234567890XXX ZZZ0987654321ZZZ [khim@localhost khim]$ $ X или ${X} — просто извлечь значение из переменной X (фигурные скобки необходимы тогда, когда после имени переменной следует буква или цифра). [khim@localhost khim]$ ptr=VAR1 [khim@localhost khim]$ echo ${!ptr} 1234567890 [khim@localhost khim]$ ptr=VAR2 [khim@localhost khim]$ echo ${!ptr} 0987654321 [khim@localhost khim]$ ${!X} — извлечь значение из переменной, имя которой хранится в переменной X. Вместе с массивами этого достаточно для создания и обработки весьма нетривиальных структур данных. [khim@localhost tmp]$ echo "${#VAR1}" 10 [khim@localhost tmp]$ echo "${VAR1:${#VAR1}-3}" 890 [khim@localhost tmp]$ ${#X} — получить длину строки X; эту операцию удобно комбинировать с извлечением подстроки. [khim@localhost tmp]$ echo ${VAR4:?can not proceed without VAR4} ; echo Ok bash: VAR4: can not proceed without VAR4 [khim@localhost tmp]$ ${X:?выражение} — извлечь значение переменной, а если она не определена, остановить выполнение скрипта. [khim@localhost tmp]$ echo "${VAR1:-ABCDEF} ${VAR3:-ABCDEF}" 1234567890 ABCDEF [khim@localhost tmp]$ echo "${VAR1:-ABCDEF} ${VAR3:-FEDCBA}" 1234567890 FEDCBA [khim@localhost tmp]$ ${X:-выражение} — условное извлечение: если переменная определена (как VAR1), используется ее значение, иначе — заданное альтернативное выражение (как в случае с VAR3). [khim@localhost tmp]$ echo "${VAR1:=ABCDEF} ${VAR3:= ABCDEF}" 1234567890 ABCDEF [khim@localhost tmp]$ echo "${VAR1: =ABCDEF} ${VAR3:=FEDCBA}" 1234567890 ABCDEF [khim@localhost tmp]$ ${X:=выражение} — то же, но альтернативное выражение становится на будущее значением переменной. [khim@localhost tmp]$ echo "${VAR1:5} ${VAR2:5:3}" 67890 543 [khim@localhost tmp]$ ${X:N1[:N2]} — извлечь из переменной X подстроку, начинающуюся с N1-го символа (и заканчивающуюся N2-м). [khim@localhost tmp]$ echo "${VAR1#*[37]} ${VAR2#*[37]} ${VAR3#*[37]}" 4567890 654321 ABCDEF [khim@localhost tmp]$ echo "${VAR1##*[37]} ${VAR2##*[37]} ${VAR3##*[37]}" 890 21 ABCDEF [khim@localhost tmp]$ echo "${VAR1%[37]*} ${VAR2%[37]*} ${VAR3%[37]*}" 123456 0987654 ABCDEF [khim@localhost tmp]$ echo "${VAR1%%[37]*} ${VAR2%%[37]*} ${VAR3%%[37]*}" 12 098 ABCDEF [khim@localhost tmp]$ ${X#шаблон}, ${X##шаблон}, S{X%шаблон}, S{X%%шаблон} — извлечь строку, удалив из нее часть, соответствующую шаблону. Шаблон строится по тем же правилам, что и для имен файлов, т. е. ‘*[37]’ — это любая последовательность символов, а затем либо ‘3’, либо ‘7’, а ‘[37]*’ — это ‘3’ или ‘7’, а затем любая последовательность символов. Операции ‘#’ и ‘%’ удаляют минимальную возможную подстроку, ‘##’ и ‘%%’ — максимальную, причем ‘#’ и ‘##’ — с начала строки, а ‘%’ и ‘%%’ — с конца. [khim@localhost tmp]$ CDPATH=/bin [khim@localhost tmp]$ CDPATH=/newpath$ {CDPATH:+:$CDPATH} [khim@localhost tmp]$ echo ${CDPATH} /newpath:/bin [khim@localhost tmp]$ unset CDPATH [khim@localhost tmp]$ CDPATH=/newpath$ {CDPATH:+:$CDPATH} [khim@localhost tmp]$ echo ${CDPATH} /newpath [khim@localhost tmp]$ ${X:+выражение} — операция, обратная условному извлечению. Может показаться мистической, но используется не так уж редко. [khim@localhost tmp]$ echo "${VAR1/[123]/x} ${VAR2/ [123]/x} ${VAR3/[123]/x}" x234567890 0987654x21 ABCDEF [khim@localhost tmp]$ echo "${VAR1//[123]/x} ${VAR2// [123]/x} ${VAR3//[123]/x}" xxx4567890 0987654xxx ABCDEF [khim@localhost tmp]$ ${X/шаблон/выражение}, ${X//шаблон/выражение} — извлечь строку, заменив в ней часть, соответствующую шаблону, заданным выражением (поиск с заменой). Операция ‘/’ выполняет замену однократно, а ‘//’ повторяет ее до победного конца. |
|
| ||||||||||||||||
|