Jaký je rozdíl mezi du -sh *
a du -sh ./*
?
Poznámka:Co mě zajímá, je *
a ./*
části.
Přijatá odpověď:
$ touch ./-c $'an12tb' foo $ du -hs * 0 a 12 b 0 foo 0 total
Jak můžete vidět, -c
soubor byl použit jako možnost du
a není hlášeno (a vidíte total
řádek kvůli du -c
). Také soubor s názvem an12tb
nás nutí si myslet, že existují soubory zvané a
a b
.
$ du -hs -- *
0 a
12 b
0 -c
0 foo
To je lepší. Tentokrát alespoň -c
není brána jako možnost.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
to je ještě lepší. ./
prefix zabraňuje -c
z toho, že je brána jako možnost, a absence ./
před b
ve výstupu označuje, že není žádné b
soubor tam, ale je tam soubor se znakem nového řádku (ale další odbočky k tomu viz níže).
Je dobrým zvykem používat ./
prefix, pokud je to možné, a pokud ne, a pro libovolná data, měli byste vždy použití:
cmd -- "$var"
nebo:
cmd -- $patterns
Pokud cmd
nepodporuje --
chcete-li označit konec možností, měli byste to nahlásit jako chybu jejímu autorovi (kromě případů, kdy je to na vlastní žádost a zdokumentováno jako u echo
).
Existují případy, kdy ./*
řeší problémy, které --
ne Například:
awk -f file.awk -- *
selže, pokud existuje soubor s názvem a=b.txt
v aktuálním adresáři (nastavuje proměnnou awk a
do b.txt
místo toho, aby mu řekl, aby soubor zpracoval).
awk -f file.awk ./*
Nemá problém, protože ./a
není platný název proměnné awk, takže ./a=b.txt
se nebere jako přiřazení proměnné.
cat -- * | wc -l
selže, pokud existuje soubor s názvem -
v aktuálním adresáři, jak to říká cat
číst z jeho stdin (-
je speciální pro většinu nástrojů pro zpracování textu a pro cd
/pushd
).
cat ./* | wc -l
je v pořádku, protože ./-
není speciální pro cat
.
Věci jako:
grep -l -- foo *.txt | wc -l
spočítat počet souborů, které obsahují foo
jsou chybné, protože předpokládá, že názvy souborů neobsahují znaky nového řádku (wc -l
počítá znaky nového řádku, které vypíše grep
pro každý soubor a soubory v samotných názvech souborů). Místo toho byste měli použít:
grep -l foo ./*.txt | grep -c /
(počítá se počet /
znaků je spolehlivější, protože pro každý soubor může být pouze jeden).
Pro rekurzivní grep
, ekvivalentní trik je použít:
grep -rl foo .//. | grep -c //
./*
může mít některé nežádoucí vedlejší účinky.
cat ./*
přidá dva další znaky na soubor, takže byste dříve dosáhli limitu maximální velikosti argumentů + prostředí. A to někdy nechcete ./
vykazovat ve výstupu. Jako:
grep foo ./*
Výstup:
./a.txt: foobar
místo:
a.txt: foobar
Další odbočky
. Cítím, že to zde musím rozvést a sledovat diskuzi v komentářích.
$ du -hs ./*
0 ./a
12 b
0 ./-c
0 ./foo
Nad tím ./
označení začátku každého souboru znamená, že můžeme jasně identifikovat, kde každý název souboru začíná (na ./
) a kde končí (na novém řádku před dalším ./
nebo konec výstupu).
To znamená, že výstup du ./*
, na rozdíl od du -- *
) lze spolehlivě analyzovat, i když ne tak snadno ve skriptu.
Když výstup jde do terminálu, existuje mnoho dalších způsobů, jak vás název souboru může oklamat:
-
Ovládací znaky, escape sekvence mohou ovlivnit způsob zobrazení věcí. Například
r
přesune kurzor na začátek řádku,b
přesune kurzor zpět,e[C
vpřed (ve většině terminálů)… -
mnoho znaků je na terminálu neviditelných, počínaje tím nejviditelnějším:znakem mezery.
-
Existují znaky Unicode, které vypadají stejně jako lomítko ve většině písem
$ printf 'u002f u2044 u2215 u2571 u29F8n' / ⁄ ∕ ╱ ⧸
(podívejte se, jak to chodí ve vašem prohlížeči).
Příklad:
$ touch x 'x ' $'ybx' $'xn0t.u2215x' $'yr0t.e[Cx'
$ ln x y
$ du -hs ./*
0 ./x
0 ./x
0 ./x
0 .∕x
0 ./x
0 ./x
Spousta x
‘s but y
chybí.
Některé nástroje jako GNU
ls by nahradilo netisknutelné znaky otazníkem (všimněte si, že ∕
(U+2215) lze tisknout), když výstup jde na terminál. GNU du
ne.
Existují způsoby, jak je přimět, aby se odhalily:
$ 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
Podívejte se, jak ∕
obrátil na ???
poté, co jsme řekli ls
že naše znaková sada byla ASCII.
$ du -hs ./* | LC_ALL=C sed -n l0t./x$0t./x $0t./x$0t.342210225x$0t./rok0t.
Linux