Четвёртое наблюдение о командной строке и путях в файловой системе

b4e428e73a1f6cb368eb139b4959b7e1.jpg

В недавно опубликованной статье «Три наблюдения о командной строке и путях в файловой системе» были рассмотрены некоторые особенности интерпретации командной строки оболочками в операционных системах Windows и Linux. Первое наблюдение было о том, что командные оболочки SH/BASH, в отличие от COMMAND/CMD, выполняют предварительную обработку параметров, содержащих шаблоны имён файлов. Перед выполнением команды параметр с шаблоном заменяется оболочкой на список параметров с именами файлов, которым этот шаблон удовлетворяет. То есть, команда:

$ cat book/chapter*.txt

перед выполнением будет преобразована командной оболочкой в команду

$ cat book/chapter1.txt book/chapter2.txt book/chapter3.txt

если в каталоге book имеются файлы chapter1.txt, chapter2.txt и chapter3.txt.
Отмечалось, что имеются доводы как в пользу такого поведения командной оболочки, так и против него. В любом случае, это уже свершившийся факт, который надо принять и научиться с ним жить. А в этой небольшой заметке приводится ещё одно наблюдение, демонстрирующее важность учёта предварительной обработки параметров-шаблонов командной оболочкой.

Допустим, в текущем каталоге ведётся разработка программы на языке Си, которая содержит заголовочный файл mysockets.h к собственной библиотеке сетевых интерфейсов mysockets.c. Тут же редактируется main.c, тут же выполняется сборка проекта командой make. В какой-то момент возникло желание посмотреть, а какие стандартные заголовочные файлы на тему сетевых сокетов установлены в системе? Для этого «не отходя от кассы» набирается такая команда:

$ find /usr/include -name *sock*.h

А в ответ — тишина. Может быть, никаких таких файлов и не установлено? Но терзают всё-таки сомнения: как так, чтобы в Linux и не было заголовочных файлов, содержащих sock в своём имени?! Проверяем:

$ ls /usr/include/linux/*sock*.h

Получаем:

/usr/include/linux/sock_diag.h
/usr/include/linux/socket.h
/usr/include/linux/sockios.h
/usr/include/linux/vm_sockets.h

Что за мистика?! Пробуем по-другому:

$ find /usr/include/linux -name *sock*.h

Опять тишина. Прямо иллюзион какой-то. Неужели программа find сломалась?

Не будем нагнетать атмосферу и выдвигать множество гипотез для объяснения наблюдаемого явления. На самом деле всё просто. И причина — в той самой предварительной обработке параметров командной оболочкой. Перед тем, как запустить программу find, командная оболочка, встретив параметр *sock*.h, содержащий подстановочные символы, проверила содержимое текущего каталога, обнаружила в нём удовлетворяющий шаблону файл mysockets.h и заботливо заменила значение параметра именем найденного файла. После этого она выполнила уже такую команду:

$ find /usr/include/linux -name mysockets.h

В дереве файловой системы /usr/include/linux файла с именем mysockets.h не нашлось, поэтому вывод команды find оказался пустым.

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

$ find /usr/include/linux -name '*sock*.h'

Этот вариант отработает в соответствии с первоначальной задумкой. А можно использовать вместо одинарных кавычек двойные? Можно, и в данном случае результат тоже будет положительным. Только надо иметь в виду, что содержимое двойных кавычек обрабатывается командной оболочкой на предмет подстановки значений переменных окружения. Можно выполнить, например, следующие две команды, и сравнить их вывод:

$ echo '$SHELL'
$SHELL
$ echo "$SHELL"
/bin/bash

Но это уже совсем другая история…

© Habrahabr.ru