Jaka jest różnica między „du -sh *” i „du -sh ./*”?


Odpowiedzi:


69
$ touch ./-c $ 'a \ n12 \ tb' foo
$ du -hs *
0 a
12 b
0 foo
0 ogółem

Jak widać, -cplik został przyjęty jako opcja dui nie jest zgłaszany (i widzisz totallinię z tego powodu du -c). Ponadto plik o nazwie a\n12\tbpowoduje, że myślimy, że istnieją pliki o nazwie ai b.

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

Tak lepiej Przynajmniej ten czas -cnie jest brany jako opcja.

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

To nawet lepiej. W ./zapobiega prefiks -cjest podejmowane jako opcja i braku ./wcześniej bw wyjściowym wskazuje, że nie ma bpliku tam, ale nie jest plikiem o znaku końca wiersza (lecz patrz poniżej 1 dalszych odchyleniami w które).

Dobrą praktyką jest używanie ./prefiksu, jeśli to możliwe, a jeśli nie, to w przypadku dowolnych danych zawsze należy używać:

cmd -- "$var"

lub:

cmd -- $patterns

Jeśli cmdnie obsługuje --oznaczenia końca opcji, należy zgłosić go jako błąd autorowi (z wyjątkiem sytuacji, gdy jest to z wyboru i udokumentowane jak dla echo).

Są przypadki, w których ./*rozwiązuje się problemy, które --nie rozwiązują problemu . Na przykład:

awk -f file.awk -- *

kończy się niepowodzeniem, jeśli a=b.txtw bieżącym katalogu znajduje się plik (ustawia zmienną awk ana b.txtzamiast nakazać jej przetworzenie pliku).

awk -f file.awk ./*

Nie ma problemu, ponieważ ./anie jest prawidłową nazwą zmiennej awk, więc ./a=b.txtnie jest traktowana jako przypisanie zmiennej.

cat -- * | wc -l

kończy się niepowodzeniem, jeśli -w bieżącym katalogu znajduje się plik wywołany , ponieważ mówi on, cataby czytać ze swojego standardowego wejścia ( -jest specjalny dla większości narzędzi do przetwarzania tekstu i do cd/ pushd).

cat ./* | wc -l

jest OK, ponieważ ./-nie jest specjalnie dla cat.

Rzeczy jak:

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

aby policzyć liczbę plików, które zawierają, foosą niepoprawne, ponieważ zakłada, że ​​nazwy plików nie zawierają znaków nowej linii ( wc -lzlicza znaki nowej linii, znaki wyjściowe grepdla każdego pliku i te w samych nazwach plików). Zamiast tego należy użyć:

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

(policzenie liczby /znaków jest bardziej niezawodne, ponieważ może być tylko jeden na nazwę pliku).

W przypadku rekurencji greprównoważną lewą jest użycie:

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

./* może jednak powodować niepożądane skutki uboczne.

cat ./*

dodaje dwa kolejne znaki do pliku, dzięki czemu szybciej osiągniesz limit maksymalnej wielkości argumentów + środowisko. A czasami nie chcesz, ./aby to było zgłaszane w danych wyjściowych. Lubić:

grep foo ./*

Wyprowadziłby:

./a.txt: foobar

zamiast:

a.txt: foobar

Dalsze dygresje

1 . Czuję, że muszę rozwinąć tę kwestię tutaj, po dyskusji w komentarzach.

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

Powyżej, ./oznaczenie początku każdego pliku oznacza, że ​​możemy jasno określić, gdzie zaczyna się każda nazwa pliku (at ./) i gdzie kończy (w nowym wierszu przed następnym ./lub końcowym wyjściem).

Oznacza to, że dane wyjściowe du ./*, w przeciwieństwie do danych wyjściowych du -- *), można analizować w wiarygodny sposób, choć nie tak łatwo w skrypcie.

Gdy dane wyjściowe trafiają do terminala, istnieje wiele innych sposobów, w jakie nazwa pliku może Cię oszukać:

  • Kontroluj postacie, sekwencje specjalne mogą wpływać na sposób wyświetlania rzeczy. Na przykład \rprzesuwa kursor na początek linii, \bprzesuwa kursor do tyłu, do \e[Cprzodu (w większości terminali) ...
  • wiele znaków jest niewidocznych na terminalu, zaczynając od najbardziej oczywistego: znaku spacji.
  • W większości czcionek są znaki Unicode, które wyglądają tak samo jak ukośnik

    $ printf '\u002f \u2044 \u2215 \u2571 \u29F8\n'
    /    

    (zobacz, jak to działa w przeglądarce).

Przykład:

$ 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

Wiele x, ale ybrakuje.

Niektóre narzędzia, takie jak GNUls, zastępują znaki niedrukowalne znakiem zapytania (zwróć uwagę, że (U + 2215) można wydrukować), gdy dane wyjściowe trafią do terminala. GNU dunie.

Istnieją sposoby, aby się ujawniły:

$ 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

Zobacz, jak się zmieniło ???po tym, jak powiedzieliśmy, lsże naszym zestawem znaków jest 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$

$oznacza koniec linii, dzięki czemu możemy rozpoznać "x"vs "x ", wszystkie znaki niedrukowalne i znaki spoza ASCII są reprezentowane przez sekwencję odwrotnego ukośnika (sam odwrotny ukośnik byłby reprezentowany przez dwa odwrotne ukośniki), co oznacza, że ​​jest jednoznaczny. To był GNU sed, powinien być taki sam we wszystkich sedimplementacjach zgodnych z POSIX, ale zauważ, że niektóre stare sedimplementacje nie są tak pomocne.

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

(nie standardowe, ale dość powszechne, także cat -Az niektórymi implementacjami). Ten jest pomocny i używa innej reprezentacji, ale jest niejednoznaczny ( "^I"i <TAB>na przykład są wyświetlane tak samo).

$ 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

Ten jest standardowy i jednoznaczny (i spójny od wdrożenia do wdrożenia), ale nie tak łatwy do odczytania.

Zauważysz, że ynigdy nie pojawił się powyżej. To jest całkowicie niezwiązane problem z du -hs *, że nie ma nic wspólnego z nazwami plików, ale należy zwrócić uwagę: bo duWykorzystanie raportów na dysku, to nie zgłasza inne linki do pliku już wymienionych (nie wszystkie duimplementacje zachowują się tak jakby gdy twarde dowiązania są wymienione w wierszu poleceń).


+1, ładne i dokładne (o ile mogę powiedzieć ^^). Szczególnie podoba mi się przewaga „grep -c /”. Warto również zauważyć: przewaga „./*” nad „*” pojawia się w jednej z (wielu) dobrych odpowiedzi na często zadawane pytania dotyczące systemu Unix (prawdopodobnie na faqs.org. Iirc, jest to pytanie o pliki rm zaczynające się od „-”).
Olivier Dulac

… I czy nie jest złą praktyką mieć pliki z nazwami nowych linii i kartami w ich nazwach? Wiem, że staram się ograniczyć nazwy do [a-z0-9.+-].
Blacklight Shining

5
@BlacklightShining, bardzo źle jest ukraść samochody, ale źle jest zostawić samochód odblokowany (ignoruj ​​nowe linie), szczególnie gdy jest to drogi samochód (skrypt uruchomiony jako użytkownik uprzywilejowany, na serwerze z wrażliwymi danymi ...) lub gdy zaparkuj go w trudnym terenie ( /tmp) lub w okolicy z dużą ilością drogich samochodów ( $HOME), a jeszcze gorzej jest przejść do strony z pytaniami i odpowiedziami i powiedzieć, że zawsze dobrze jest nie zamykać samochodu bez określenia warunków (w zamkniętym garażu, skrypt napisałeś prowadzony sam na maszynie niepodłączonej do żadnej sieci lub pamięci wymiennej ...)
Stéphane Chazelas

1
@BlacklightShining, znaki nowej linii są niezwykłe, ale obecnie osadzone spacje są bardzo powszechne, szczególnie w przypadku plików tworzonych za pomocą GUI.
Alexis

2
@BlacklightShining, tak, chociaż ten (jak "b "lub "a\bb") oszukałby użytkownika na terminalu, ale nie skrypt analizujący wynik du ./*. Powinienem chyba dodać o tym notatkę. Zrobię jutro. Zauważ, że wcześniej miałem na myśli uprzywilejowane w ogólnym sensie, a nie root(chociaż rootoczywiście dotyczy to tym bardziej ). nowe linie są dozwolone, ignorowanie ich jest błędem. robaki mają zwyczaj wykorzystywania. Musisz mierzyć ryzyko indywidualnie dla każdego przypadku. Dobra praktyka kodowania może w wielu przypadkach uniknąć problemów. Z pewnością w SE powinniśmy podnieść świadomość.
Stéphane Chazelas

6

Nie ma różnicy między a *a ./*pod względem tego, które pliki będą na liście. Jedyną różnicą byłaby druga postać, każdy plik miałby przed sobą ukośnik kropkowy ./, co zwykle oznacza bieżący katalog.

Pamiętaj, że .katalog jest skrótem dla bieżącego katalogu.

$ 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

Możesz przekonać się, że te dwie listy są zasadniczo takie same, używając, echoaby zobaczyć, do czego powłoka je rozszerzy.

$ echo *
$ echo ./*

Te 2 polecenia wyświetlą listę wszystkich plików w bieżącym katalogu.

Przykłady

Możemy zrobić fałszywe dane, takie jak:

$ 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

Teraz, gdy używamy powyższych echopoleceń, widzimy następujące dane wyjściowe:

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

Ta różnica może wydawać się niepotrzebna, ale są sytuacje, w których chcesz zagwarantować różnym narzędziom wiersza poleceń systemu Unix, że przekazujesz im nazwy plików za pośrednictwem wiersza polecenia i nic więcej!

Więc po co używać ./*?

Jak wskazuje odpowiedź @ Stephane , ze względu na charakter znaków, które są dozwolone podczas nazywania plików i katalogów w Uniksie, można tworzyć niebezpieczne nazwy plików, które mają nieoczekiwane skutki uboczne, gdy są przekazywane do różnych poleceń Uniksa w wierszu poleceń.

Tak często użycie ./zostanie użyte, aby zagwarantować, że rozszerzone nazwy plików będą traktowane jako nazwy plików, gdy zostaną przekazane jako argumenty do różnych poleceń Uniksa.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.