Відповіді:
$ touch ./-c $ 'a \ n12 \ tb' foo $ du -hs * 0 а 12 б 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
Вище, що ./
маркування початку кожного файлу означає, що ми можемо чітко визначити, звідки починається (at ./
) кожне ім'я файлу та де воно закінчується (у новому рядку до наступного ./
чи кінця виводу).
Це означає, що результат 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
's, але y
його немає.
Деякі інструменти, такі як GNU
l, замінять символи, що не друкуються, на знак запитання (зауважте, що ∕
(U + 2215) є надрукованим), коли вихід спрямований на термінал. ГНУ 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, що ви передаєте їм назви файлів через командний рядок, і більше нічого!
Як вказується у відповіді @ Стефана , через характер того, які символи є законними при іменуванні файлів і каталогів в Unix, можуть бути побудовані небезпечні назви файлів, які мають несподівані побічні ефекти, коли вони передаються різним командам Unix у командному рядку.
Тому часто використання ./
буде використано для гарантії того, що розширені назви файлів розглядаються як імена файлів, коли передаються як аргументи різним командам Unix.