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

В недавно опубликованной статье «Три наблюдения о командной строке и путях в файловой системе» были рассмотрены некоторые особенности интерпретации командной строки оболочками в операционных системах 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

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

@R0bur
11.01.2024 21:44 UTC
Первоисточник

Комментарии

@saboteur_kiev
11.01.2024 17:37 UTC
+13

Не, вы серьезно?

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

Давайте я еще сразу расскажу, что поиск всех файлов в линукс - это *, а в дос это *.* (потому что имя и расширение это в fat16 разные поля directory entry, и Windows как наследница DOS до сих пор поддерживает это для обратной совместимости.

Но это все вытекает из общего правила как идет разрешение wildcard в командной строке Linux, и правил кавычек. Пожалуйста, горшочек не вари.

@R0bur
11.01.2024 18:29 UTC
-2

Давайте я еще сразу расскажу, что поиск всех файлов в линукс - это *, а в дос это . (потому что имя и расширение это в fat16 разные поля directory entry, и Windows как наследница DOS до сих пор поддерживает это для обратной совместимости.

А можно подробнее? Только что выполнил в Windows команду " DIR * " - отобразились все файлы. Сделал то же самое в эмуляторе DosBox - тоже отобразились. Или Вы имеете в виду, что надо именно в 16-разрядном MS DOS выполнять? Но ведь MS DOS мы не обсуждаем?

11.01.2024 19:21 UTC
+4

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

@R0bur
11.01.2024 18:32 UTC
+1

А, кажется понял. Вы имеете в виду, что в Windows маска *.* удовлетворяет всем файлам, тогда как в Linux - только тем, которые содержат в имени "точку". Ну да, ну да.

@omgiafs
12.01.2024 05:20 UTC
+3

Я буду ещё категоричнее. Это прямо в мануале bash описано.

Недавно поймал себя на мысли, что 95% всех околоайтишных статей - это художественные пересказы отдельных абзацев мануалов различного ПО.

Не такого "распространения знаний" хотели создатели Интернета.

@APh
13.01.2024 08:28 UTC
0

Ну, в NTFS расширений уже нет. А суффикс "точка, три символа" --- дань привычке, удобству, совместимости. Просто звёздочка должна в Уиндоуз все файлы подтащить, не?

13.01.2024 08:58 UTC
0

А если я хочу подтащить файлы в которых есть точка?

13.01.2024 09:22 UTC
+1

Если хотите, чтобы маска "*.*" обрабатывалась как в SH/BASH, то в Windows воспользуйтесь оболочкой PowerShell.

14.01.2024 19:51 UTC
+1

Не обязательно. Можно на Windows поиметь BusyBox, CygWin или MinGW или целый w64devkit, куда это всё входит и пользоваться вполне себе POSIX-средой.

14.01.2024 19:57 UTC
0

Ну, сходу в cmd я бы воспользовался чем-то таким:
where /r . * | find "."

Для примера я создал в директории несколько файлов. Вот, что вывела команда dir и что указанная команда. Во втором случае нет файла file. https://ibb.co/QC25HpK

15.01.2024 17:09 UTC
0

Ну, в NTFS расширений уже нет.

Зато они по-прежнему есть и играют важнейшую роль для загрузчика у MS — он всё ещё считает, что тип файла определяется его “расширением” (поубивал бы...)

21.01.2024 09:31 UTC
+1

)) Не спорю, что последняя часть имени файла из точки и трёх символов может иметь важное значение для унаследованного кода.

Я о другом. О том, что нет у NTFS поля данных "расширение", как у FAT-12, -16, например. Точка и три символа на конце -- часть поля данных "имя". Потому, называются, просто, суффиксом. И символ точка, потому, часть имени. В FAT-12 точка не хранилась. Её и не зачем было бы хранить там!!! Она не входила ни в одно из двух полей данных, а была разделителем при синтаксическом разборе.

21.01.2024 21:45 UTC
+1

он всё ещё считает, что тип файла определяется его “расширением” (поубивал бы...)

А почему бы и нет?
В fat/ntfs нет поля, которое бы указывало на тип, есть только аттрибут "файл" или "директория". Поэтому или читать сигнатуры (что долго), или расширение.
Вполне нормальная концепция, весьма гибкая.
В *nix своя концепция, и расширение там прикрутили уже только в GUI, но не как часть ОС.

@Batalmv
11.01.2024 17:44 UTC
+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 19:24 UTC
+2

давайте еще накинем

$ touch 1 2 .1 .
$ ls *
1  2
$ ls .*
.1 .2
$ ls -A.1 .2 1 2

11.01.2024 20:16 UTC
0

Это автор в прошлой статье затронул

А вот тут поинтереснее разыменование :) Вроде и в кавычках, но нет - разыменовывает :)

Это я к тому, что выдирать куски из доки не очень хорошо, а так вульгарно - и вообще крайне вредно для тех, кто будет читать :)

@0Bannon
11.01.2024 22:46 UTC
0

Спасибо. Интересно было прочитать.