Odpowiedzi:
$ touch ./-c $ 'a \ n12 \ tb' foo $ du -hs * 0 a 12 b 0 foo 0 ogółem
Jak widać, -c
plik został przyjęty jako opcja du
i nie jest zgłaszany (i widzisz total
linię z tego powodu du -c
). Ponadto plik o nazwie a\n12\tb
powoduje, że myślimy, że istnieją pliki o nazwie a
i b
.
$ du -hs -- *
0 a
12 b
0 -c
0 foo
Tak lepiej Przynajmniej ten czas -c
nie jest brany jako opcja.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
To nawet lepiej. W ./
zapobiega prefiks -c
jest podejmowane jako opcja i braku ./
wcześniej b
w wyjściowym wskazuje, że nie ma b
pliku 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 cmd
nie 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.txt
w bieżącym katalogu znajduje się plik (ustawia zmienną awk a
na b.txt
zamiast nakazać jej przetworzenie pliku).
awk -f file.awk ./*
Nie ma problemu, ponieważ ./a
nie jest prawidłową nazwą zmiennej awk, więc ./a=b.txt
nie 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, cat
aby 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ą, foo
są niepoprawne, ponieważ zakłada, że nazwy plików nie zawierają znaków nowej linii ( wc -l
zlicza znaki nowej linii, znaki wyjściowe grep
dla 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 grep
ró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
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ć:
\r
przesuwa kursor na początek linii, \b
przesuwa kursor do tyłu, do \e[C
przodu (w większości terminali) ...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 y
brakuje.
Niektóre narzędzia, takie jak GNU
ls, zastępują znaki niedrukowalne znakiem zapytania (zwróć uwagę, że ∕
(U + 2215) można wydrukować), gdy dane wyjściowe trafią do terminala. GNU du
nie.
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 sed
implementacjach zgodnych z POSIX, ale zauważ, że niektóre stare sed
implementacje 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 -A
z 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 y
nigdy 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 du
Wykorzystanie raportów na dysku, to nie zgłasza inne linki do pliku już wymienionych (nie wszystkie du
implementacje zachowują się tak jakby gdy twarde dowiązania są wymienione w wierszu poleceń).
[a-z0-9.+-]
.
/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 ...)
"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ż root
oczywiś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ść.
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, echo
aby zobaczyć, do czego powłoka je rozszerzy.
$ echo *
$ echo ./*
Te 2 polecenia wyświetlą listę wszystkich plików w bieżącym katalogu.
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 echo
poleceń, 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!
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.