Книга: 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
Сообщение ls: таких файлов нет
x*y not found
$ >xyzzy
Создать файл xyzzy
$ ls x*y
Файл xyzzy соответствует x*y
xyzzy
$ ls 'х*y'
ls не интерпретирует *
x*y not found
$
Появление обратной дробной черты в конце строки требует продолжения строки, что является способом задать интерпретатору очень длинную строку:
$ 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
не сможет предупредить вас, что вы собираетесь удалить все ваши файлы?
- 3.1 Структура командной строки
- 3.2 Метасимволы
- 3.3 Создание новых команд
- 3.4 Аргументы и параметры команд
- 3.5 Результат выполнения программы в качестве аргумента
- 3.6 Переменные языка shell
- 3.7 Еще раз о переключении ввода-вывода
- 3.8 Циклы в shell-программах
- 3.9 Программа bundle: соберем все воедино
- 3.10 Для чего нужно программировать на языке shell!
- Глава 3 Возможности интерпретатора shell
- 12.7.2. Раскрытие имени файла: glob() и globfree()
- Поиск файлов с использованием регyлярных выражений
- Атрибуты файлов и управление каталогами
- 4.1 Семейство программ grep
- 3.9 Программа bundle: соберем все воедино
- Chapter 5. Kernel Initialization
- Кто такая Елена Ивашенцева?