Книга: UNIX — универсальная среда программирования

3.2 Метасимволы

3.2 Метасимволы

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

$ echo *

есть не что иное, как некое подобие команды ls. В гл. 1 мы не отметили, что во избежание проблем с именами '.' и '..', которые присутствуют в любом каталоге, символы подстановки в именах файлов нельзя применять к именам файлов, начинающимся с точки. Правило таково: символы подстановки в именах файлов действуют на имена файлов, начинающихся с точки, только в том случае, если точка явно задана в шаблоне. Как обычно, "рассудительная" команда echo прояснит ситуацию:

$ ls
.profile
junk
temp
$ echo *
junk temp
$ echo .*
. .. .profile
$

Символы со специальным значением, подобные *, называются метасимволами. Существует множество метасимволов (в табл. 3.1 приводится их полный список, но некоторые символы мы обсудим только в гл. 5).

> prog > file — переключить стандартный выходной поток в файл
>> prog >> file — добавить стандартный выходной поток к файлу
< prog < file — извлечь стандартней выходной поток из файла
| p1 | p2 — передать стандартный выходной поток p1 как стандартный выходной поток для p2
<<str "Документ здесь": стандартный выходной поток задается в последующих строках до строки, состоящей из одного символа str
* Задает любую строку, состоящую из нуля или более символов, в имени файла
? Задает любой символ в имени файла
[ccc] Задает любой символ из [ccc] в имени файла (допустимы диапазоны, такие, как 0-9 или a-z)
; Завершает команды: p1; p2 — выполнить p1, затем p2
& Выполняет аналогичные функции, но не ждет окончания p1
`...` Инициирует выполнение команд(ы) в ...;`...` заменяется своим стандартным выводом
(...) Инициирует выполнение команд(ы) в ... в порожденном shell
{...} Инициирует выполнение команд(ы) в ... в текущем вызове shell (используется редко)
$1, $2, ... Заменяются аргументами командного файла
$var Значение переменной var в программе на языке shell
${var} Значение var; исключает коллизии в случае конкатенации переменной с последующим текстом (см. также табл. 5.3)
  c — использовать непосредственно символ c, перевод строки отбрасывается
'...' Означает непосредственное использование
"..." Означает непосредственное использование, но после того, как $, `...` и будут интерпретированы
# В начале слова означает, что вся остальная строка рассматривается как комментарий (но не в седьмой версии)
var=value Присваивает value переменной var
p1 && p2 Предписывает выполнить p1; в случае успеха выполнить p2
p1 || p2 Предписывает выполнить p1; в случае неудачи выполнить p2

Таблица 3.1: Метасимволы shell

При таком количестве метасимволов интерпретатора необходимо иметь возможность экранировать специальный символ от интерпретации. Самый простой и надежный способ экранирования — заключить его в апострофы:

$ echo '* * *'
* * *
$

Можно также использовать кавычки "...", но интерпретатор на самом деле "заглядывает" внутрь этих кавычек в поиске метасимволов $, '...' и , так что не применяйте "...", если только вам не требуется определенным образом обработать строку в кавычках.

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

$ echo ***

Хотя строка *** не похожа на английское слово, в терминологии языка shell это слово, ибо им является любая последовательность символов, воспринимаемая интерпретатором как целое, включая даже пробелы, если они взяты в кавычки.

Кавычки одного вида могут экранировать кавычки другого вида:

$ echo "Don't do that!"
Don't do that!
$

и могут не заключать в себе весь аргумент:

$ echo x'*'y
x*y
$ echo '*'A'?'
*А?
$

В последнем примере команда echo получает один аргумент, не содержащий апострофов, так как, сделав свое дело, апострофы исчезают. Строки в кавычках могут содержать символы строк:

$ echo 'hello
> world'
hello
world
$

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

Во всех приведенных выше примерах экранирование специальных символов предохраняет их от интерпретации. Команда

$ echo x*y

выдает все имена файлов, начинающиеся с x и кончающиеся y. Как обычно, команда echo ничего "не знает" ни о файлах, ни о метасимволах; интерпретация *, если она требуется, осуществляется shell.

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

$ ls x*y
x*y not found
Сообщение ls: таких файлов нет

$ >xyzzy      Создать файл xyzzy

$ ls x*y
xyzzy        
Файл xyzzy соответствует x*y

$ ls 'х*y'
x*y not found 
ls не интерпретирует *

$

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

$ echo abc
> def
> ghi
abcdefghi
$

Обратите внимание на то, что символ перевода строки отбрасывается, если перед ним стоит обратная дробная черта, но он остается, если взят в кавычки. Метасимвол # в программе на языке shell практически всюду используется в качестве комментария; если слово начинается с #, остаток строки игнорируется:

$ echo hello#there
hello
$ echo hello # there
hello # there
$

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

Упражнение 3.2

Объясните результат выполнения команды

$ ls .

Некоторые дополнительные сведения о команде echo

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

$ правильное эхо введенная команда:
Введенная команда: $
Нет завершающего перевода строки

Однако при таком решении в самой распространенной ситуации, когда перевод строки нужен, он не подразумевается по умолчанию и требует дополнительного ввода:

$ правильное эхо 'Привет!
>'
Привет!
$

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

Но как быть, если это нежелательно? В седьмой версии системы команда echo имеет единственный флаг -n, который подавляет последний символ перевода строки:

$ echo -n Enter a command:
Enter a command: $
Приглашение на той же строке

$ echo -
-                 
Только - является специальным случаем

$

Существует одна маленькая хитрость в случае получения эха от -n, за которым должен следовать символ перевода строки:

$ echo -n '-n
>'
-n
$

Такое решение некрасиво, но эффективно, к тому же это довольно редкий случай.

Другой подход принят в System V, где команда echo интерпретирует последовательность символов с обратной дробной чертой аналогично тому, как это делается в языке Си, а именно: b обозначает "шаг назад", c подавляет перевод строки (правда, здесь не очень точно воспроизведена конструкция Си):

$ echo 'Введенная команда: с' Версия System V

Введенная команда: $

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

Итак, обе реализации команды echo имеют и положительные, и отрицательные стороны. Мы будем использовать вариант седьмой версии (-n), поэтому, если ваша команда echo выполняется по-другому, несколько приводимых ниже примеров потребуют незначительных изменений.

Возникает еще один, философский, вопрос: что должна делать команда, если ей не передали аргументов, в частности, следует ли ей выдавать пустую строку или вообще ничего не предпринимать? Как вы уже знаете, все настоящие реализации команды выдают пустую строку, но в ранних версиях все было иначе. По этому поводу велись большие дебаты, а Д. МакИлрой привнес в них даже элемент мистицизма.

UNIX и Эхо

Жила-была в стране Нью-Джерси UNIX, прекрасная девушка, к которой приезжали издалека, чтобы полюбоваться ею. Ослепленные чистотой UNIX, все искали ее руки и сердца: одни — за изящество, другие — за изысканную вежливость, третьи — за проворность при выполнении самых изнурительных заданий. Была она от рождения столь великодушна и услужлива, что все женихи остались довольны ею, а ее многочисленное потомство распространилось во все концы земли.

Сама природа покровительствовала UNIX и вторила ей более охотно, чем кому-либо из смертных. Простые люди поражались ее эхом, таким оно было точным и кристально чистым. Они не могли поверить, что ей отвечают те же леса и скалы, которые так искажают их собственные голоса. Когда один нетерпеливый пастушок попросил UNIX: "Пусть эхо ответит ничего", и она послушно открыла рот, эхо промолчало. "Зачем ты открываешь рот?" — спросил пастушок. — "Отныне никогда не открывай его, если эхо должно ответить ничего!" — и UNIX подчинилась.

"Но я хочу совершенного исполнения, даже если эхо отвечает ничего," — потребовал другой, обидчивый, юноша, — "а никакого совершенного эха не получится при закрытом рте". Не желая обидеть никого из них, UNIX согласилась говорить разные "ничего" для нетерпеливого и обидчивого юношей. Она называла "ничего" для обидчивого как 'n'. Однако теперь, когда она говорила 'n', на самом деле она не произносила ничего, поэтому ей приходилось открывать рот дважды: один раз, чтобы сказать 'n', и второй раз, чтобы не сказать ничего. Это не понравилось обидчивому юноше, который тотчас сказал: "Для меня 'n' звучит, как настоящее "ничего", но когда ты открываешь рот второй раз, то все портишь. Возьми второе "ничего" назад". Услужливая UNIX согласилась отказаться от некоторых эхо и обозначила это как 'c'. С тех пор обидчивый юноша мог услышать совершенное эхо "ничего", если он задавал 'n' и 'c' вместе, но говорят, что он так и не услышал его, поскольку умер от излишеств в обозначениях.

Упражнение 3.3

Предскажите, что сделает команда grep в каждом случае, а затем проверьте себя;

grep $     grep
grep $    grep \
grep \$  grep "$"
grep '$'   grep '"$'
grep ''$   grep "$"

Файл, состоящий из таких команд, послужит хорошим материалом для теста, если вы хотите поэкспериментировать.

Упражнение 3.4

Как указать grep, что нужно найти шаблон, начинающийся с '-'? Почему взятие аргумента в кавычки не помогает? Подсказка: исследуйте флаг .

Упражнение 3.5

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

$ echo */*

Может ли она вывести все имена всех каталогов? В каком порядке появятся эти имена?

Упражнение 3.6

(Хитрый вопрос.) Как ввести / в локальное имя файла (т.е. символ /, который не является разделителем компонентов в абсолютном имени)?

Упражнение 3.7

Что произойдет в случае ввода команд $ cat x y >y и $ cat x >>x

Подумайте, прежде чем броситься их выполнять.

Упражнение 3.8

Если вы введете

$ rm *

почему команда rm не сможет предупредить вас, что вы собираетесь удалить все ваши файлы?

Оглавление книги


Генерация: 1.852. Запросов К БД/Cache: 3 / 1
поделиться
Вверх Вниз