почему ls -d также перечисляет файлы и где это задокументировано?

48
  • при указании ls --directory a*следует указывать только каталоги, начинающиеся сa*
  • НО это перечисляет файлы И каталоги, начинающиеся с a

Вопросы :

  • где я могу найти какую-то документацию по этому вопросу, кроме как manи infoгде, я думаю, я внимательно посмотрел?
  • это работает только в BASH?
erch
источник
1
Смотрите также этот другой очень похожий вопрос .
Стефан Шазелас
5
echo a*также покажет вам файлы, начинающиеся с a(если есть). Просто чтобы прояснить, что это не lsделает это, но удар.
ash108

Ответы:

89

a*И *a*синтаксис реализуется командным, а не по lsкоманде.

Когда вы печатаете

ls a*

По приглашению вашей оболочки оболочка расширяется a*до списка всех существующих файлов в текущем каталоге, имена которых начинаются с a. Например, он может расширяться a*до последовательности a1 a2 a3и передавать их в качестве аргументов ls. Сама lsкоманда никогда не видит *персонажа; он видит только три аргумента a1, a2и a3.

В целях расширения подстановочного знака «файлы» относятся ко всем объектам в текущем каталоге. Например, это a1может быть обычный файл, a2каталог a3или символическая ссылка. Все они имеют записи каталога, и расширению подстановочного знака оболочки не важно, к какому объекту относятся эти записи.

Практически во всех оболочках, через которые вы можете встретиться (bash, sh, ksh, zsh, csh, tcsh, ...), используются символы подстановки. Детали могут различаться, но основной синтаксис *соответствия нуля или более символов и ?соответствия любому отдельному символу является достаточно последовательным.

В частности, для bash это описано в разделе «Расширение имени файла» руководства по bash; запустите « info bashПоиск по расширению имени» или посмотрите здесь .

Тот факт, что это делается оболочкой, а не отдельными командами, имеет некоторые интересные (а иногда и удивительные) последствия. Лучшее в этом то, что подстановочные знаки согласованы (почти) для всех команд; если оболочка этого не сделает, некоторые команды неизбежно не будут беспокоиться, а другие сделают это немного иначе, чем, по мнению автора, «лучше». (Я думаю, что у командной оболочки Windows есть эта проблема, но я недостаточно знаком с ней, чтобы комментировать дальше.)

С другой стороны, сложно написать команду для переименования нескольких файлов. Если вы напишите:

mv *.log *.log.bak

он, вероятно, потерпит неудачу, поскольку *.log.bakрасширяется на основе файлов, которые уже существуют в текущем каталоге. Есть команды, которые делают такие вещи, но они должны использовать свой собственный синтаксис, чтобы указать, как файлы должны быть переименованы. Некоторые команды (такие как find) могут делать свое собственное расширение подстановочного знака; Вы должны процитировать аргументы для подавления расширения оболочки:

find . -name '*.txt' -print

Расширение подстановочного знака оболочки полностью основано на синтаксисе аргумента командной строки и набора существующих файлов. Это не может быть затронуто значением команды. Например, если вы хотите переместить все .logфайлы в родительский каталог, введите:

mv *.log ..

Если вы забыли ..:

mv *.log

и .logв текущем каталоге окажется ровно два файла, он расширится до:

mv one.log two.log

который переименует one.logи ударит two.log.

РЕДАКТИРОВАТЬ : И после 52 голосов, принять и значок Гуру, может быть, я действительно должен ответить на вопрос в названии.

-dИли --directoryвариант lsне сказать это только список каталогов. Это говорит ему перечислять каталоги так же, как и они сами, а не их содержимое. Если вы задаете имя каталога в качестве аргумента ls, по умолчанию он будет перечислять содержимое каталога, поскольку это обычно то, что вас интересует. -dОпция указывает ему перечислить только сам каталог. Это может быть особенно полезно в сочетании с подстановочными знаками. Если вы введете:

ls -l a*

lsпредоставит вам длинный список каждого файла , имя которого начинается с a, и содержимого каждого каталога, имя которого начинается с a. Если вы просто хотите получить список файлов и каталогов, по одной строке для каждого, вы можете использовать:

ls -ld a*

что эквивалентно:

ls -l -d a*

Помните еще раз, что lsкоманда никогда не видит *персонажа.

Что касается того, где это задокументировано, man lsпокажет вам документацию для lsкоманды практически на любой Unix-подобной системе. В большинстве систем на основе Linux lsкоманда является частью пакета GNU coreutils; если у вас есть infoкоманда, либо info lsили info coreutils lsдолжны дать вам более точную и полную документацию. Другие системы, такие как MacOS, могут использовать другие версии lsкоманды и могут не иметь infoкоманды; для этих систем используйте man ls. И ls --helpпокажет относительно короткое сообщение об использовании (117 строк в моей системе), если вы используете реализацию GNU coreutils.

И да, даже специалистам нужно время от времени обращаться к документации. Смотрите также эту классическую шутку .

Кит Томпсон
источник
8
@Thomas: a*расширяется до списка всех файлов в текущем каталоге , имена которых начинаются с a.
Кит Томпсон
2
@Thomas: Или в любом другом каталоге, который вы укажете:ls subdir/a*
Кит Томпсон,
1
Чтобы реально увидеть команду, выполняемую после расширения оболочки, echoэто. Поэтому, если вы хотите знать, что происходит, когда вы это делаете ls a*, сначала выполнитеecho ls a*
Карлос Кампдеррос
1
или просто echo a*... оболочка все равно расширяется
Бесполезно
2
@sendmoreinfo: это зависит от оболочки и настроек. В csh и tcsh сбой расширения glob является ошибкой. В bash shopt -s failglobвызывает то же поведение. Установка, nullglobно не failglobприводит, например, *nosuchfile*к расширению до пустой строки.
Кит Томпсон
27

Смотрите ответ Кита Томпсона; но объяснить, почему ls --directory a*отображаются файлы и каталоги: --directoryопция не подавляет файлы, не относящиеся к каталогам. Вместо этого он перечисляет каталоги как таковые, в то время как в противном случае он перечисляет их содержимое. Пример:

$ mkdir foo
$ touch foo/bar
$ ls foo
bar
$ ls --directory foo
foo
chirlu
источник
3
этот пример более убедителен с -Fопцией
Гленн Джекман
6

Чтобы быть очень явным, это задокументировано на странице руководства ls (1) :

-d, --directory перечисляет записи каталога вместо содержимого и не разыменовывает символические ссылки

чтобы быть справедливым, «записи вместо содержания», вероятно, может быть более объяснительным:

Если FILE является каталогом, покажите саму запись каталога вместо того, чтобы перечислить содержимое этого каталога. Если ФАЙЛ является символической ссылкой, покажите саму запись ссылки вместо файла, на который указывает ссылка.

MSW
источник
Чтобы быть педантичным, в то время как опция -d может быть объяснена (если кратко) на странице man, расширение аргументов и подстановочные знаки не описаны на странице man ls, так как это делается оболочкой. Если вы отключите расширение имени файла в своей оболочке, «ls a *» покажет вам только файл с буквальным названием «a *».
Джонни
Чтобы быть педантичным, в то время как другие респонденты ответили на расширение оболочки, ни один из них не рассматривает скудное объяснение и то, как оно может быть неправильно истолковано. Если вы хотите, чтобы я повторил то, что уже было хорошо сказано, будьте более откровенны.
MSW
4

подстановка

Как уже было объяснено, расширение *(и подобные расширения) называется globbing в жаргоне Unix и обычно является функцией командного процессора (в переводе Unix на языке Unix это называется «оболочка»). Таким образом, глобализация может быть использована и во многих других местах; введите man 7 globв оболочке классического дистрибутива Linux (или посмотрите это ), чтобы узнать больше о globbing.

В ранних версиях Unix globфактически была реализована отдельная программа, которая называлась /etc/glob(см. Стр. 10 этого старого руководства UNIX для документации по этому вопросу). В настоящее время это подпрограмма кода, предоставляемая библиотеками кода, и она обычно используется оболочками. Источник : Википедия .

Справочники

Что касается того, почему ls -dсписки файлов, а также каталоги ...

Страница lsman объясняет, почему это происходит, но с очень кратким объяснением. Итак, вот попытка расширенного объяснения:

По умолчанию lsперечисляет содержимое каталогов, когда им даны их имена, и будет делать то же самое для символических ссылок на каталоги. В -dопции означает, «когда дано имя (ы) каталог (страна)» (которые также могут быть заданы неявно с помощью globБинга) , показывает только _name_s каталога (страна), но не их содержание. Точно так же, когда предоставляется (явно или неявно ) имя (я) символических ссылок на каталоги , показывается имя файла символической ссылки, а не содержимое каталога, на который она ссылается.

-dНасколько я могу судить, эта опция не имеет никакого отношения к тому, какие элементы перечислены; это можно сделать (источник: здесь ) с find, например , так: find . -maxdepth 1 -type d. Я не уверен, есть ли хороший способ сделать это только с помощью GNU ls. Вот несколько примеров использования findкоманды.

Итак, подведем итог: по умолчанию lsпоказывает содержимое каталогов, когда дан их путь. -dизменяет это поведение, показывая только имя каталога, как это будет сделано для стандартного файла.

Abbafei
источник
3

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

> {ice} ~ :10:47 % ls -l / 
total 97
drwxr-xr-x   2 root root  4096 May  3 00:27 bin
drwxr-xr-x   4 root root  1024 May  3 14:17 boot
drwxr-xr-x   2 root root  4096 Apr 29 13:44 cdrom
drwxr-xr-x  18 root root  4420 May  9 09:58 dev
...
lrwxrwxrwx   1 root root    33 May  3 14:16 vmlinuz -> boot/vmlinuz-3.9.0-030900-generic
lrwxrwxrwx   1 root root    29 May  3 11:07 vmlinuz.old -> boot/vmlinuz-3.8.0-19-generic
> {ice} ~ :10:47 % ls -ld /
drwxr-xr-x 25 root root 4096 May  3 14:16 /
defhlt
источник
2

Документация является частью оболочки . В частности, оболочка выполняет различные расширения и замены в определенном порядке перед выполнением команды.

Последовательность слов расширений для оболочки , совместимой с Bourne является

  1. Расширение Тильды
  2. Расширение параметра
  3. Подстановка команд
  4. Арифметическое Расширение
  5. Разделение поля
  6. Расширение пути
  7. Удаление цитаты

Таким образом, lsкоманда даже не видит *символы, аргументы уже расширены всеми файлами, соответствующими a*шаблону glob. Это непохоже на Windows, где каждая команда, нуждающаяся в перемещении имени файла, должна реализовать эту функцию сама.

Jens
источник
1

Ключевое слово, которое вам нужно ( man bash) - «Расширение пути».

Хауке Лагинг
источник
3
больше похоже на «Расширение пути» или «Генерация имени файла». Относится к «сопоставлению с образцом», но не то же самое.
Стефан Шазелас
@StephaneChazelas Действительно, но «сопоставление с образцом» является подразделом «раскрытия пути» на странице руководства, тогда как «Генерация имени файла» там вообще не встречается (только в информационном документе).
Hauke ​​Laging