Pull to refresh

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

Level of difficultyEasy
Reading time5 min
Views7.1K

Наблюдение № 1

Первое наблюдение будет как раз о шаблонах, но не мышления, а о шаблонах имён файлов и различиях в их трактовке командными интерпретаторами DOS/Windows и Unix/Linux.

Допустим, в дереве файловой системы есть такая ветвь:

…\folder\
…\folder\file1.txt
…\folder\subfolder\
…\folder\subfolder\file2.txt

Для эксперимента в Windows её можно создать командами:

> MKDIR folder\subfolder
> ECHO 1 > folder\file1.txt
> ECHO 2 > folder\subfolder\file2.txt

а в Linux - командами:

$ mkdir -p folder/subfolder
$ echo 1 > folder/file1.txt
$ echo 2 > folder/subfolder/file2.txt

Предположим, возникла необходимость просмотреть содержимое каталога folder, для чего в Windows была набрана команда:

> DIR /B folder\*

Ключ /B требует отображать только имена файлов, без дополнительных сведений о дате, типе и тому подобных деталях.
Вот, что эта команда выведет:

file1.txt
subfolder

То, что требовалось и ожидалось. Теперь выполним аналогичную команду в командной оболочке BASH под Linux:

$ ls -1 folder/*

Ключ -1 (цифра «один», а не строчная латинская буква «эль») требует осуществлять вывод в одну колонку, но дело не в этом, а в самом содержимом этого вывода:

folder/file1.txt
folder/subfolder:
file2.txt

Ладно бы ещё только имена файлов и каталогов предварялись родительским folder — с этим ещё можно мириться. Но зачем команда отобразила содержимое подкаталога folder/subfolder?

Оказывается, командные оболочки Windows (COMMAND.COM и CMD.EXE) и Linux (SH и BASH, в других оболочках дело может обстоять иначе) по-разному обрабатывают параметры командной строки, которые содержат подстановочные символы * и ?. Windows поступает просто — передаёт параметры командам/программам в том виде, в каком они записаны в командной строке. Раз написано folder/*, то команда DIR и получит folder/*, а дальше сама попробует понять, чего от неё хотят. Раз *, значит надо показать все элементы каталога folder. Всё просто и логично.

В Linux всё сложнее. Командная оболочка сначала анализирует параметры командной строки и, если в них нет подстановочных символов или параметры заключены в кавычки, то поступает как Windows. Если же параметры содержат подстановочные символы и в кавычки не заключены, то командная оболочка сама проверяет соответствие содержимого каталога указанному шаблону и заменяет параметр с шаблоном на список параметров с подходящими именами файлов. В приведенном примере после такой обработки командная строка

$ ls -l folder/*

превратится в:

$ ls -l folder/file1.txt folder/subfolder

Программа ls выводит информацию о своих параметрах, причём если это каталог, то по умолчанию отображает его содержимое. Между прочим, Windows ведёт себя схожим образом, если в команде DIR указать несколько параметров:

> DIR /B folder\file1.txt folder\subfolder
file1.txt
file2.txt

Особенность обработки шаблонов путей к файлам при использовании команды ls может вызвать раздражение, но не приводит к непоправимым последствиям. В ином же контексте может произойти более неприятная история.

Причина различий теперь ясна, но как в Linux получить список содержимого каталога без разворачивания содержимого подкаталогов? Можно просто указать имя каталога, без маски:

$ ls -1 folder
file1.txt
subfolder

Отлично, но как быть, если к именам элементов каталога (файлам и подкаталогам) всё-таки надо применить маску, например, «*.abc»? Идея заключить параметр командной строки в кавычки "folder/*" не работает: программа ls (в Linux это внешняя утилита, а не внутренняя команда оболочки, как DIR в Windows) ожидает, что шаблон будет обработан командной оболочкой, и просто сообщает об отсутствии в каталоге folder элемента с именем "*". Так что остаётся воспользоваться специальным ключом -d программы ls:

$ ls -1d folder/*
folder/file1.txt
folder/subfolder

Бытует мнение, что вариант работы командных оболочек Linux более прогрессивный, чем Windows. Ведь оболочка берёт на себя «общую» часть работы по разбору путей к файлам, в результате чего упрощается код программ и стандартизируется их поведение. Это действительно так, пока шаблону удовлетворяют несколько файлов. Но что, если таких файлов 100500? Командная оболочка попытается единовременно создать командную строку, в которой перечислены пути ко всем этим файлам. Обычно это заканчивается нехваткой памяти для командной строки. В то время, как подход, принятый в Windows, позволяет программе спокойно перебирать все файлы по одному.

Наблюдение № 2

Второе наблюдение связано с именованием каталогов в Linux. Имя subfolder может означать как файл, так и каталог. Несмотря на то, что в Linux почти все сущности являются файлами, различия иногда бывают важны. Допустим, требуется создать копию файла file1.txt в подкаталоге subfolder. Это можно сделать такой командой:

$ cp folder/file1.txt folder/subfolder

Она работает, и всё вроде бы прекрасно. Но предположим, что в путь назначения вкралась опечатка: вместо subfolder набрано sunfolder. Что произойдёт в этом случае? Операция завершится успешно, но содержимое файла file1.txt будет записано в файл folder/sunfolder. Это, очевидно, не то, что ожидалось. Как можно избежать такой неприятности?

Один из вариантов — для указания целевого каталога использовать автодополнение по клавише Tab. Но в этом случае надо быть особенно внимательным, если в каталоге folder есть другие элементы с именами, похожими на subfolder. Автоматика может выбрать не ту цель, которая была запланирована, и промах будет не менее досадным.

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

$ cp folder/file1.txt folder/subfolder/

В таком варианте опечатка уже не пройдёт незамеченной:

$ cp folder/file1.txt folder/sunfolder/
cp: cannot create regular file 'folder/sunfolder': Not a directory

Таким образом, если ведущая наклонная черта в пути к файлу говорит о том, что путь начинается от корня файловой системы, то заключительная наклонная черта подчёркивает, что путь ведёт не к файлу, а к каталогу.

Наблюдение № 3

Третье наблюдение касается «особых каталогов». Все знают, что родительский каталог обозначается двумя точками: «..». Многим известно, что одной точкой «.» обозначается текущий каталог. Но если на каталоги верхнего уровня приходится ссылаться регулярно, то польза от ссылки на текущий каталог не столь очевидна: он ведь и так должен подразумеваться по умолчанию.

Теоретически должен, но на практике не всегда так происходит. Например, в текущем каталоге лежит файл moria.deb, который надо установить. Следующая команда с этой задачей не справится:

$ sudo apt install moria.deb
...
E: Unable to locate package moria.deb
E: Couldn't find any package by glob 'moria.deb'
E: Couldn't find any package by regex 'moria.deb'

Можно возразить, что для работы с локальными пакетами следует использовать программу dpkg, а не apt, которая задумывалась для работы с пакетами в репозиториях и ожидает в командной строке название пакета, а не имя файла. Но, с другой стороны, небольшое изменение командной строки позволяет раскрыть и программе apt глаза и на локальные файлы:

$ sudo apt install ./moria.deb

В отличие от dpkg, такой вариант автоматически подхватит из репозиториев недостающие зависимости, если такие вдруг обнаружатся в ходе установки.

Ещё одна польза от «точки» обнаруживается, когда надо выполнить копирование содержимого каталога, в котором есть скрытые файлы или подкаталоги (в Linux таковыми являются файлы, первым символом в имени которых идёт точка). Примером может служить файловая система дистрибутивов Ubuntu, в корне которой которой присутствует каталог .disk со служебной информацией. Если образ дистрибутивного носителя примонтирован в точке /mnt/ISO, то следующая команда копирования разместит в целевом каталоге ~/ubuntu копии всех файлов и каталогов, кроме скрытого:

$ cp -a /mnt/ISO/* ~/ubuntu/

А скрытый каталог потребуется переписать явным образом:

$ cp -a /mnt/ISO/.disk ~/ubuntu/

Но можно воспользоваться «точкой» и выполнить всю необходимую работу одной командой:

$ cp -a /mnt/ISO/. ~/ubuntu/

Этот вариант выполнит полное копирование, включая скрытый каталог.

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

Tags:
Hubs:
Total votes 14: ↑9 and ↓5+8
Comments23

Articles