$ touch ./-c $ 'a \ n12 \ tb' foo
$ du -hs *
0 а
12 б
0 фу
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нужно читать из его стандартного ввода ( -это специально для большинства утилит обработки текста и 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, которые выглядят так же, как и косая черта
(посмотрите, как это происходит в вашем браузере).
Пример:
$ 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не хватает.
Некоторые инструменты, такие как GNUls, заменяют непечатаемые символы знаком вопроса (обратите внимание, что ∕(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, он должен быть одинаковым во всех sedреализациях, совместимых с POSIX, но обратите внимание, что некоторые старые sedреализации не так полезны.
(не стандартный, но довольно распространенный, также cat -Aс некоторыми реализациями). Это один полезно и использует другое представление , но неоднозначно ( "^I"и <TAB>отображается то же, например).
$ du -hs ./*| od -vtc
00000000 \t ./ x \n 0 \t ./ x \n 0 \t .0000020/ x \n 0 \t .342210225 x \n 0 \t ./ y
0000040 \r 0 \t .033[ C x \n 0 \t ./ y \b x
0000060 \n
0000061
Этот стандарт является стандартным и однозначным (и последовательным от реализации к реализации), но не так легко читается.
Вы заметите, что yникогда не показывалось выше. Это совершенно не связанная с этим проблемаdu -hs * , которая не имеет ничего общего с именами файлов, но следует отметить: поскольку она duсообщает об использовании диска, она не сообщает о других ссылках на уже указанный файл (не все duреализации ведут себя так, хотя при перечислении жестких ссылок) в командной строке).
+1, хороший и тщательный (насколько я могу сказать ^^). Я особенно люблю преимущество "grep -c /". Также стоит отметить: преимущество "./*" над "*" появляется в одном из (многих) хороших ответов на часто задаваемые вопросы по Unix (вероятно, на faqs.org. Iirc, речь идет о вопросе о файлах, начинающихся с "-").
Оливье Дюлак
... и не плохо ли иметь файлы с символами новой строки и вкладками в именах? Я знаю, что пытаюсь ограничить имена [a-z0-9.+-].
Blacklight Shining
5
@ BlacklightShining, очень плохо воровать машины, но плохо оставлять машину незапертой (игнорировать новые строки), особенно когда это дорогая машина (скрипт запускается как привилегированный пользователь, на сервере с конфиденциальными данными ...) или когда вы припаркуйте его в неровной зоне ( /tmp) или в районе с большим количеством дорогих автомобилей ( $HOME), и еще хуже - зайти на сайт вопросов и ответов и сказать, что всегда хорошо не блокировать свой автомобиль, не указав, в каких условиях (в закрытом гараже, сценарий Вы написали, что запускаете самостоятельно только на машине, не подключенной к какой-либо сети или съемному хранилищу ...)
Стефан Шазелас
1
@ BlacklightShining, переводы строк необычны, но встроенные пробелы очень распространены в настоящее время, особенно для файлов, созданных с помощью графического интерфейса пользователя.
Алексис
2
@ BlacklightShining, да, хотя этот (например, "b "или "a\bb") будет обманывать пользователя на терминале, но не скрипт, анализирующий вывод du ./*. Я, вероятно, должен добавить примечание об этом. Сделаю завтра. Обратите внимание, что раньше я имел в виду привилегированный в общем смысле, а не root(хотя, rootконечно, это относится и к тем более ). переводы строки разрешены, игнорирование их является ошибкой. жуки имеют привычку эксплуатироваться. Вы должны измерить риск в каждом конкретном случае. Хорошая практика кодирования может избежать проблем во многих случаях. Конечно, на SE, мы должны повысить осведомленность.
Стефан Шазелас
6
Нет разницы между a *и ./*с точки зрения того, какие файлы будут перечислены. Единственная разница будет со 2-й формой, каждый файл будет иметь ./префикс косой черты перед ними, что обычно означает текущий каталог.
Помните, что .каталог является сокращенной записью для текущего каталога.
$ ls -la | head -4
total 28864
drwx------.104 saml saml 12288Jan2320:04.
drwxr-xr-x.4 root root 4096Jul82013..-rw-rw-r--.1 saml saml 972Oct620:26 abcdefg
Вы можете убедить себя, что эти 2 списка по сути одно и то же, используя, echoчтобы увидеть, к чему оболочка их расширит.
$ echo *
$ echo ./*
Эти 2 команды выведут список всех файлов в вашем текущем каталоге.
Примеры
Мы можем сделать некоторые поддельные данные, например, так:
Это различие может показаться ненужным, но есть ситуации, когда вы хотите гарантировать различным инструментам командной строки Unix, что вы передаете им имена файлов через командную строку, и ничего более!
Тогда зачем использовать ./*?
Как указывает ответ @ Stephane , из-за того, какие символы являются допустимыми при именовании файлов и каталогов в Unix, могут быть созданы опасные имена файлов, которые имеют неожиданные побочные эффекты, когда они передаются различным командам Unix в командной строке.
Очень часто использование ./будет использоваться для гарантии того, что расширенные имена файлов будут рассматриваться как имена файлов при передаче в качестве аргументов различным командам Unix.
Ответы:
Как видите,
-c
файл был выбран как опцияdu
и не сообщается (и вы видитеtotal
строку из-заdu -c
). Кроме того, вызванный файлa\n12\tb
заставляет нас думать, что есть файлы с именемa
иb
.Так-то лучше. По крайней мере, это время
-c
не принимается в качестве опции.Это даже лучше. В
./
префикс предотвращает-c
от принимаются в качестве опции и отсутствие./
до того,b
в выходной указывает на то, что нетb
файла в наличии, но есть файл с символом новой строки (но смотри ниже 1 для дальнейших отступлений на том , что).Рекомендуется использовать
./
префикс, когда это возможно, а если нет и для произвольных данных, вы всегда должны использовать:или:
Если
cmd
не поддерживается--
отметка конца опций, вы должны сообщить об этом автору как об ошибке (кроме случаев, когда это выбрано и задокументировано, напримерecho
).Есть случаи, когда
./*
решает проблемы, которые--
этого не делают. Например:завершается неудачно, если
a=b.txt
в текущем каталоге вызывается файл (задает для переменной awka
значениеb.txt
вместо того, чтобы указывать ей обрабатывать файл).Не имеет проблемы, потому что
./a
не является допустимым именем переменной awk, поэтому./a=b.txt
не воспринимается как присвоение переменной.происходит сбой, если
-
в текущем каталоге вызывается файл , так как он говорит, чтоcat
нужно читать из его стандартного ввода (-
это специально для большинства утилит обработки текста иcd
/pushd
).это нормально, потому что
./-
не является особенным дляcat
.Вещи как:
подсчитывать количество файлов, содержащихся
foo
неверно, так как предполагается, что имена файлов не содержат символов новой строки (wc -l
подсчитывает символы новой строки, те, которые выводятсяgrep
для каждого файла и те, что указаны в самих именах файлов). Вы должны использовать вместо:(подсчет количества
/
символов является более надежным, поскольку может быть только один для каждого имени файла).Для рекурсивного
grep
, эквивалентный трюк должен использовать:./*
может иметь некоторые нежелательные побочные эффекты, хотя.добавляет еще два символа в файл, поэтому вы быстрее достигнете предела максимального размера аргументов + окружения. И иногда вы не хотите,
./
чтобы об этом сообщалось в выводе. Подобно:Будет вывод:
вместо того:
Дальнейшие отступления
1 . Я чувствую, что должен остановиться на этом здесь, после обсуждения в комментариях.
Выше,
./
отметка начала каждого файла означает, что мы можем четко определить, где начинается каждое имя файла (в./
) и где оно заканчивается (в новой строке перед следующим./
или концом вывода).Это означает, что выходные данные
du ./*
, в отличие от данных,du -- *
могут быть надежно проанализированы, хотя и не так легко в сценарии.Когда вывод идет на терминал, есть еще много способов, которыми имя файла может обмануть вас:
\r
перемещает курсор в начало строки,\b
перемещает курсор назад,\e[C
вперед (в большинстве терминалов) ...В большинстве шрифтов есть символы Unicode, которые выглядят так же, как и косая черта
(посмотрите, как это происходит в вашем браузере).
Пример:
Много
x
, ноy
не хватает.Некоторые инструменты, такие как
GNU
ls, заменяют непечатаемые символы знаком вопроса (обратите внимание, что∕
(U + 2215), тем не менее, можно печатать), когда вывод отправляется на терминал. GNUdu
нет.Есть способы заставить их проявить себя:
Посмотрите, к чему
∕
обратились???
после того, как мы сказали,ls
что наш набор символов был ASCII.$
помечает конец строки, так что мы можем заметить"x"
vs"x "
, все непечатаемые символы и символы не ASCII представлены последовательностью обратной косой черты (сама обратная косая черта будет представлена двумя обратными косыми чертами), что означает, что она однозначна. Это был GNUsed
, он должен быть одинаковым во всехsed
реализациях, совместимых с POSIX, но обратите внимание, что некоторые старыеsed
реализации не так полезны.(не стандартный, но довольно распространенный, также
cat -A
с некоторыми реализациями). Это один полезно и использует другое представление , но неоднозначно ("^I"
и<TAB>
отображается то же, например).Этот стандарт является стандартным и однозначным (и последовательным от реализации к реализации), но не так легко читается.
Вы заметите, что
y
никогда не показывалось выше. Это совершенно не связанная с этим проблемаdu -hs *
, которая не имеет ничего общего с именами файлов, но следует отметить: поскольку онаdu
сообщает об использовании диска, она не сообщает о других ссылках на уже указанный файл (не всеdu
реализации ведут себя так, хотя при перечислении жестких ссылок) в командной строке).источник
[a-z0-9.+-]
./tmp
) или в районе с большим количеством дорогих автомобилей ($HOME
), и еще хуже - зайти на сайт вопросов и ответов и сказать, что всегда хорошо не блокировать свой автомобиль, не указав, в каких условиях (в закрытом гараже, сценарий Вы написали, что запускаете самостоятельно только на машине, не подключенной к какой-либо сети или съемному хранилищу ...)"b "
или"a\bb"
) будет обманывать пользователя на терминале, но не скрипт, анализирующий выводdu ./*
. Я, вероятно, должен добавить примечание об этом. Сделаю завтра. Обратите внимание, что раньше я имел в виду привилегированный в общем смысле, а неroot
(хотя,root
конечно, это относится и к тем более ). переводы строки разрешены, игнорирование их является ошибкой. жуки имеют привычку эксплуатироваться. Вы должны измерить риск в каждом конкретном случае. Хорошая практика кодирования может избежать проблем во многих случаях. Конечно, на SE, мы должны повысить осведомленность.Нет разницы между a
*
и./*
с точки зрения того, какие файлы будут перечислены. Единственная разница будет со 2-й формой, каждый файл будет иметь./
префикс косой черты перед ними, что обычно означает текущий каталог.Помните, что
.
каталог является сокращенной записью для текущего каталога.Вы можете убедить себя, что эти 2 списка по сути одно и то же, используя,
echo
чтобы увидеть, к чему оболочка их расширит.Эти 2 команды выведут список всех файлов в вашем текущем каталоге.
Примеры
Мы можем сделать некоторые поддельные данные, например, так:
Теперь, когда мы используем вышеупомянутые
echo
команды, мы видим следующий вывод:Это различие может показаться ненужным, но есть ситуации, когда вы хотите гарантировать различным инструментам командной строки Unix, что вы передаете им имена файлов через командную строку, и ничего более!
Тогда зачем использовать ./*?
Как указывает ответ @ Stephane , из-за того, какие символы являются допустимыми при именовании файлов и каталогов в Unix, могут быть созданы опасные имена файлов, которые имеют неожиданные побочные эффекты, когда они передаются различным командам Unix в командной строке.
Очень часто использование
./
будет использоваться для гарантии того, что расширенные имена файлов будут рассматриваться как имена файлов при передаче в качестве аргументов различным командам Unix.источник