Яка різниця між "du -sh *" і "du -sh ./*"?


Відповіді:


69
$ 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його немає.

Деякі інструменти, такі як GNUl, замінять символи, що не друкуються, на знак запитання (зауважте, що (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реалізації поводяться так , хоча , коли жорсткі посилання перераховані у командному рядку).


+1, приємно і ретельно (наскільки я можу сказати ^^). Я особливо люблю перевагу "grep -c /". Також варто відзначити: перевага "./*" над "*" з'являється в одній з (багатьох) хороших відповідей на поширені запитання Unix (можливо, на faqs.org. Iirc, мова йде про питання про rm-ing файли, починаючи з a "-").
Олів’є Дулак

… І не погана практика мати файли з новими рядками та вкладками на їх імена? Я знаю, що намагаюся обмежити імена [a-z0-9.+-].
Blacklight Shining

5
@BlacklightShining, викрадати машини дуже погано , але погано залишати машину розблокованою (ігнорувати нові рядки), особливо коли це дорогий автомобіль (сценарій працює як привілейований користувач, на сервері з конфіденційними даними ...) або коли ви припаркувати його в нерівній зоні ( /tmp) або на ділянці з великою кількістю дорогих автомобілів ( $HOME), і ще гірше зайти на сайт із запитаннями і сказати, що це завжди добре, щоб не заблокувати машину, не уточнюючи, в яких умовах (у закритому гаражі, сценарій ви писали, що ви працюєте самостійно лише на машині, не підключеній до жодної мережі чи знімного сховища ...)
Stéphane Chazelas

1
@BlacklightShining, нові рядки є незвичними, але вбудовані пробіли сьогодні дуже поширені, особливо для файлів, створених за допомогою GUI.
alexis

2
@BlacklightShining, так, хоча той (як "b "або "a\bb") обдурить користувача в терміналі, але не сценарій, що аналізує вихід du ./*. Мені, мабуть, слід додати примітку про це. Зробимо завтра. Зауважте, що раніше я мав на увазі привілейований у загальному сенсі, але не root(хоча це стосується, rootзвичайно, більше). нові рядки дозволені, ігнорування їх - помилка. клопи мають звичку експлуатуватися. Ви повинні вимірювати ризик у кожному конкретному випадку. Гарна практика кодування може уникнути проблем у багатьох випадках. Безумовно, щодо SE ми повинні підвищити рівень обізнаності.
Стефан Шазелас

6

Немає різниці між 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.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.