$ 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
$ 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.
(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
00000000 \t ./ x \n 0 \t ./ x \n 0 \t .0000020/ x \n 0 \t .342210225 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 12288Jan2320:04.
drwxr-xr-x.4 root root 4096Jul82013..-rw-rw-r--.1 saml saml 972Oct620: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.
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.
Odpowiedzi:
Jak widać,
-c
plik został przyjęty jako opcjadu
i nie jest zgłaszany (i widzisztotal
linię z tego powodudu -c
). Ponadto plik o nazwiea\n12\tb
powoduje, że myślimy, że istnieją pliki o nazwiea
ib
.Tak lepiej Przynajmniej ten czas
-c
nie jest brany jako opcja.To nawet lepiej. W
./
zapobiega prefiks-c
jest podejmowane jako opcja i braku./
wcześniejb
w wyjściowym wskazuje, że nie mab
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ć:lub:
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 dlaecho
).Są przypadki, w których
./*
rozwiązuje się problemy, które--
nie rozwiązują problemu . Na przykład:kończy się niepowodzeniem, jeśli
a=b.txt
w bieżącym katalogu znajduje się plik (ustawia zmienną awka
nab.txt
zamiast nakazać jej przetworzenie pliku).Nie ma problemu, ponieważ
./a
nie jest prawidłową nazwą zmiennej awk, więc./a=b.txt
nie jest traktowana jako przypisanie zmiennej.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 docd
/pushd
).jest OK, ponieważ
./-
nie jest specjalnie dlacat
.Rzeczy jak:
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ściowegrep
dla każdego pliku i te w samych nazwach plików). Zamiast tego należy użyć:(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:./*
może jednak powodować niepożądane skutki uboczne.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ć:Wyprowadziłby:
zamiast:
Dalsze dygresje
1 . Czuję, że muszę rozwinąć tę kwestię tutaj, po dyskusji w komentarzach.
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ściowychdu -- *
), 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
(zobacz, jak to działa w przeglądarce).
Przykład:
Wiele
x
, aley
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. GNUdu
nie.Istnieją sposoby, aby się ujawniły:
Zobacz, jak się
∕
zmieniło???
po tym, jak powiedzieliśmy,ls
że naszym zestawem znaków jest ASCII.$
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ł GNUsed
, powinien być taki sam we wszystkichsed
implementacjach zgodnych z POSIX, ale zauważ, że niektóre staresed
implementacje nie są tak pomocne.(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).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 zdu -hs *
, że nie ma nic wspólnego z nazwami plików, ale należy zwrócić uwagę: bodu
Wykorzystanie raportów na dysku, to nie zgłasza inne linki do pliku już wymienionych (nie wszystkiedu
implementacje zachowują się tak jakby gdy twarde dowiązania są wymienione w wierszu poleceń).źródło
[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 wynikdu ./*
. Powinienem chyba dodać o tym notatkę. Zrobię jutro. Zauważ, że wcześniej miałem na myśli uprzywilejowane w ogólnym sensie, a nieroot
(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.Możesz przekonać się, że te dwie listy są zasadniczo takie same, używając,
echo
aby zobaczyć, do czego powłoka je rozszerzy.Te 2 polecenia wyświetlą listę wszystkich plików w bieżącym katalogu.
Przykłady
Możemy zrobić fałszywe dane, takie jak:
Teraz, gdy używamy powyższych
echo
poleceń, widzimy następujące dane wyjściowe: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.źródło