В недавно опубликованной статье «Три наблюдения о командной строке и путях в файловой системе» были рассмотрены некоторые особенности интерпретации командной строки оболочками в операционных системах 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
Но это уже совсем другая история...
Комментарии (12)
Batalmv
11.01.2024 17:37+2Я вам еще подкину :)
/home/mobaxterm/tt> ll
total 0
-rw-r--r-- 1 mbatal UsersGrp 0 Jan 11 18:39 111
-rw-r--r-- 1 mbatal UsersGrp 0 Jan 11 18:39 222
/home/mobaxterm/tt> for i in '*'; do echo $i; done
111 222
/home/mobaxterm/tt> for i in *; do echo $i; done
111
222-----------------
Я думаю вдумчивое чтение man bash (1): GNU Bourne-Again SHell (manpages.org) вам откроет еще столько всего :)
saboteur_kiev
11.01.2024 17:37+2давайте еще накинем
$ touch 1 2 .1 . $ ls * 1 2 $ ls .* .1 .2 $ ls -A.1 .2 1 2
Batalmv
11.01.2024 17:37Это автор в прошлой статье затронул
А вот тут поинтереснее разыменование :) Вроде и в кавычках, но нет - разыменовывает :)
Это я к тому, что выдирать куски из доки не очень хорошо, а так вульгарно - и вообще крайне вредно для тех, кто будет читать :)
saboteur_kiev
Не, вы серьезно?
То, что если вы не хотите раскрывать маски, нужно брать в кавычки, рассмотрено уже в инете во всех углах, включая в комментах в прошлой статье. Зачем ради этого еще целую статью делать?
Давайте я еще сразу расскажу, что поиск всех файлов в линукс - это *, а в дос это *.* (потому что имя и расширение это в fat16 разные поля directory entry, и Windows как наследница DOS до сих пор поддерживает это для обратной совместимости.
Но это все вытекает из общего правила как идет разрешение wildcard в командной строке Linux, и правил кавычек. Пожалуйста, горшочек не вари.
R0bur Автор
А можно подробнее? Только что выполнил в Windows команду " DIR * " - отобразились все файлы. Сделал то же самое в эмуляторе DosBox - тоже отобразились. Или Вы имеете в виду, что надо именно в 16-разрядном MS DOS выполнять? Но ведь MS DOS мы не обсуждаем?
saboteur_kiev
Подробнее - у вас в windows * и *.* работает примерно одинаково.
А в линукс *.* - найти имена, в которых есть хотя бы одна точка.
R0bur Автор
А, кажется понял. Вы имеете в виду, что в Windows маска *.* удовлетворяет всем файлам, тогда как в Linux - только тем, которые содержат в имени "точку". Ну да, ну да.
omgiafs
Я буду ещё категоричнее. Это прямо в мануале bash описано.
Недавно поймал себя на мысли, что 95% всех околоайтишных статей - это художественные пересказы отдельных абзацев мануалов различного ПО.
Не такого "распространения знаний" хотели создатели Интернета.
APh
Ну, в NTFS расширений уже нет. А суффикс "точка, три символа" --- дань привычке, удобству, совместимости. Просто звёздочка должна в Уиндоуз все файлы подтащить, не?
saboteur_kiev
А если я хочу подтащить файлы в которых есть точка?
R0bur Автор
Если хотите, чтобы маска "*.*" обрабатывалась как в SH/BASH, то в Windows воспользуйтесь оболочкой PowerShell.