В чем разница между «du -sh *» и «du -sh ./*»?

В чем разница между du -sh * и du -sh ./*?

Примечание: меня интересует * и ./*.

25 голосов | спросил Biswanath 24 Jpm1000000pmFri, 24 Jan 2014 15:53:49 +040014 2014, 15:53:49

2 ответа


65
$ touch ./-c $ 'a \ n12 \ tb' foo
$ du -hs *
 0 a
12 b 
0 foo
 0 всего 

Как вы можете видеть, файл -c был взят как опция для du и не сообщается (и вы видите строку total из-за du -c). Кроме того, файл с именем a\n12\tb заставляет нас думать, что есть файлы с именем a и b.

 $ du -hs -- *
0       a
12      b
0       -c
0       foo

Это лучше. По крайней мере на этот раз -c не принимается как опция.

 $ du -hs ./*
0       ./a
12      b
0       ./-c
0       ./foo

Это еще лучше. Префикс ./ предотвращает выполнение -c как опция и отсутствие ./ до b на выходе указывает, что там нет файла b, но есть файл с символом новой строки (но см. ниже 1 для дальнейших отступлений от этого).

Рекомендуется использовать префикс ./, если нет, и для произвольных данных вы должны всегда использовать:

 cmd -- "$var"

или

 cmd -- $patterns

Если cmd не поддерживает --, чтобы отметить конец опций, вы должны сообщать об этом как об ошибке его автору (за исключением случаев, когда он по выбору и документирован как для echo).

Бывают случаи, когда ./* решает проблемы, которые -- нет. Например:

 awk -f file.awk -- *

не удается, если в текущем каталоге есть файл с именем a=b.txt (устанавливает переменную awk a до b.txt вместо того, чтобы сообщать об этом процессу).

 awk -f file.awk ./*

Не проблема, потому что ./a не является допустимым именем переменной awk, поэтому ./a=b.txt не принимается за назначение переменной.

 cat -- * | wc -l

не удается, если в текущем каталоге есть файл с именем -, так как он сообщает cat для чтения из его stdin (- является специальным для большинства утилит обработки текста и cd /pushd)

.
 cat ./* | wc -l

в порядке, потому что ./- не относится к cat.

Вещи вроде:

 grep -l -- foo *.txt | wc -l

, чтобы подсчитать количество файлов, содержащих foo, неверны, поскольку предполагается, что имена файлов не содержат символов новой строки (wc -l подсчитывает символы новой строки, выходные данные grep для каждый файл и имена самих файлов). Вместо этого вы должны использовать:

 grep -l foo ./*.txt | grep -c /

(подсчет числа символов / более надежный, так как может быть только одно имя файла).

Для рекурсивного grep эквивалентный трюк заключается в использовании:

 grep -rl foo .//. | grep -c //

./* могут иметь некоторые нежелательные побочные эффекты.

 cat ./*

добавляет еще двасимвола на файл, так что раньше вы достигнете предела максимального размера аргументов + среды. И иногда вы не хотите, чтобы этот ./ отображался в результатах. Как:

 grep foo ./*

Вывести:

 ./a.txt: foobar

вместо:

 a.txt: foobar

Дальнейшие отступления

1 . Мне кажется, что я должен расширить это здесь, после обсуждения в комментариях.

 $ du -hs ./*
0       ./a
12      b
0       ./-c
0       ./foo

Выше, этот ./, обозначающий начало каждого файла, означает, что мы можем четко определить, где начинается каждое имя файла (в ./) и где он заканчивается (в новой строке перед следующим ./ или конец вывода).

Это означает, что вывод du ./*, в отличие от вывода du -- *) может быть проанализирован надежно, хотя и не так легко в скрипте.

Когда вывод идет на терминал, но есть еще много способов, чтобы имя файла могло обмануть вас:

  • Управляющие символы, escape-последовательности могут влиять на то, как отображаются объекты. Например, \r перемещает курсор в начало строки, \b перемещает курсор назад, \e[C вперед (в большинстве терминалов) ...
  • многие символы невидимы на терминале, начиная с самого очевидного: пробельный символ.
  • Есть символы Unicode, которые выглядят точно так же, как и косая черта в большинстве шрифтов

     $ printf '\u002f \u2044 \u2215 \u2571 \u29F8\n'
    / ⁄ ∕ ╱ ⧸
    

    (см., как это происходит в вашем браузере).

Пример:

 $ touch x 'x ' $'y\bx' $'x\n0\t.\u2215x' $'y\r0\t.\e[Cx'
$ ln x y
$ du -hs ./*
0       ./x
0       ./x
0       ./x
0       .∕x
0       ./x
0       ./x

Много x, но y

Некоторые инструменты, такие как GNU ls заменят непечатаемые символы вопросительным знаком (обратите внимание, что (U + 2215) можно печатать, хотя), когда выход идет на терминал. GNU du не делает.

Есть способы заставить себя раскрыть себя:

 $ ls
x  x   x?0?.∕x  y  y?0?.?[Cx  y?x
$ LC_ALL=C ls
x  x?0?.???x  x   y  y?x  y?0?.?[Cx

Посмотрите, как обратился к ??? после того, как мы сказали ls, что наш набор символов был ASCII.

 $ du -hs ./* | LC_ALL=C sed -n l
0\t./x$
0\t./x $
0\t./x$
0\t.\342\210\225x$
0\t./y\r0\t.\033[Cx$
0\t./y\bx$

$ отмечает конец строки, поэтому мы можем определить "x" vs "x ", все непечатаемые символы и символы, отличные от ASCII, представлены последовательностью обратного слэша ( обратная косая черта будет представлена ​​двумя обратными косыми чертами), что означает, что она однозначна. Это был GNU sed, он должен быть одинаковым во всех совместимых с POSIX sed, но обратите внимание, что некоторые старые реализации sed не так полезны.

 $ du -hs ./* | cat -vte
0^I./x$
0^I./x $
0^I./x$
0^I.M-bM-^HM-^Ux$

(не стандартный, но довольно общий, также cat -A с некоторыми реализациями). Это полезно и использует другое представление, но неоднозначно ("^I" и <TAB> отображаются одинаково, например).

 $ du -hs ./* | od -vtc
0000000   0  \t   .   /   x  \n   0  \t   .   /   x      \n   0  \t   .
0000020   /   x  \n   0  \t   . 342 210 225   x  \n   0  \t   .   /   y
0000040  \r   0  \t   . 033   [   C   x  \n   0  \t   .   /   y  \b   x
0000060  \n
0000061

Этот стандарт является стандартным и недвусмысленным (и является последовательным от реализации к реализации), но не так легко читается.

Вы заметите, что y никогда не появлялся выше. Это совершенно не связанный issue с du -hs *, который не имеет ничего общего с именами файлов, но должен быть отмечен: поскольку du сообщает об использовании диска, он не сообщает о других ссылках на уже указанный файл (не все du ведут себя так, хотя если жесткие ссылки указаны в командной строке).

ответил Stéphane Chazelas 24 Jpm1000000pmFri, 24 Jan 2014 16:23:05 +040014 2014, 16:23:05
6

Нет разницы между * и ./* с точки зрения того, какие файлы будут перечислены. Единственное различие было бы в 2-й форме, каждый файл имел бы префикс с надписью ./, который обычно означает текущий каталог.

Помните, что каталог . является сокращенным обозначением для текущего каталога.

$ ls -la | head -4
total 28864
drwx------. 104 saml saml    12288 Jan 23 20:04 .
drwxr-xr-x.   4 root root     4096 Jul  8  2013 ..
-rw-rw-r--.   1 saml saml      972 Oct  6 20:26 abcdefg

Вы можете убедить себя, что эти 2 списка, по сути, одно и то же, используя echo, чтобы узнать, к чему будет распространяться оболочка.

$ echo *
$ echo ./*

Эти 2 команды будут перечислять все файлы в вашем текущем каталоге.

Примеры

Мы можем сделать некоторые поддельные данные следующим образом:

$ touch file{1..5}
$ ll
total 0
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file1
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file2
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file3
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file4
-rw-rw-r--. 1 saml saml 0 Jan 24 07:14 file5

Теперь, когда мы используем приведенные выше команды echo, мы видим следующий вывод:

$ echo *
file1 file2 file3 file4 file5
$ echo ./*
./file1 ./file2 ./file3 ./file4 ./file5

Это отличие может показаться излишним, но есть ситуации, когда вы хотите гарантировать различные инструменты командной строки Unix, которые вы передаете им через командную строку, и ничего больше!

Итак, зачем использовать ./*?

Как @ ответ Стефана указывает , из-за характера того, какие символы являются законными, когда именование файлов & каталоги в Unix, могут быть созданы опасные имена файлов, которые имеют неожиданные побочные эффекты, когда они передаются в различные команды Unix в командной строке.

Поэтому часто использование ./ будет использоваться, чтобы гарантировать, что расширенные имена файлов считаются именами файлов при передаче в качестве аргументов различные команды Unix.

ответил slm 24 Jpm1000000pmFri, 24 Jan 2014 16:17:38 +040014 2014, 16:17:38

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132