Ответы:
$ 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 -- *
могут быть надежно проанализированы, хотя и не так легко в сценарии.
Когда вывод идет на терминал, есть еще много способов, которыми имя файла может обмануть вас:
\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
, он должен быть одинаковым во всех sed
реализациях, совместимых с POSIX, но обратите внимание, что некоторые старые 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
никогда не показывалось выше. Это совершенно не связанная с этим проблемаdu -hs *
, которая не имеет ничего общего с именами файлов, но следует отметить: поскольку она du
сообщает об использовании диска, она не сообщает о других ссылках на уже указанный файл (не все du
реализации ведут себя так, хотя при перечислении жестких ссылок) в командной строке).
[a-z0-9.+-]
.
/tmp
) или в районе с большим количеством дорогих автомобилей ( $HOME
), и еще хуже - зайти на сайт вопросов и ответов и сказать, что всегда хорошо не блокировать свой автомобиль, не указав, в каких условиях (в закрытом гараже, сценарий Вы написали, что запускаете самостоятельно только на машине, не подключенной к какой-либо сети или съемному хранилищу ...)
"b "
или "a\bb"
) будет обманывать пользователя на терминале, но не скрипт, анализирующий вывод du ./*
. Я, вероятно, должен добавить примечание об этом. Сделаю завтра. Обратите внимание, что раньше я имел в виду привилегированный в общем смысле, а не root
(хотя, root
конечно, это относится и к тем более ). переводы строки разрешены, игнорирование их является ошибкой. жуки имеют привычку эксплуатироваться. Вы должны измерить риск в каждом конкретном случае. Хорошая практика кодирования может избежать проблем во многих случаях. Конечно, на SE, мы должны повысить осведомленность.
Нет разницы между a *
и ./*
с точки зрения того, какие файлы будут перечислены. Единственная разница будет со 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, что вы передаете им имена файлов через командную строку, и ничего более!
Как указывает ответ @ Stephane , из-за того, какие символы являются допустимыми при именовании файлов и каталогов в Unix, могут быть созданы опасные имена файлов, которые имеют неожиданные побочные эффекты, когда они передаются различным командам Unix в командной строке.
Очень часто использование ./
будет использоваться для гарантии того, что расширенные имена файлов будут рассматриваться как имена файлов при передаче в качестве аргументов различным командам Unix.