web-dev-qa-db-ja.com

「du -sh *」と「du -sh ./*」の違いは何ですか?

du -sh *du -sh ./*の違いは何ですか?

注:興味があるのは*./*の部分です。

26
Biswanath
 $ touch ./-c $ 'a\n12\tb' foo 
 $ du -hs * 
0 a 
 12 b
 0 foo 
合計0

ご覧のように、-cファイルはduのオプションとして取得されたため報告されません(du -cのためにtotal行が表示されます)。また、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 さらなる補足については)。

可能な場合は./接頭辞を使用することをお勧めします。そうでない場合、および任意のデータの場合は、alwaysを使用する必要があります。

cmd -- "$var"

または:

cmd -- $patterns

cmdがオプションの終わりを示す--をサポートしていない場合は、そのバグを作成者にバグとして報告する必要があります(ただし、選択によりechoのように文書化されている場合は除きます)。

./*--が解決しない問題を解決する場合があります。例えば:

awk -f file.awk -- *

現在のディレクトリにa=b.txtというファイルがある場合は失敗します(ファイルを処理するように指示するのではなく、awk変数ab.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 /

/文字の数を数えると、ファイル名ごとに1つしか存在できないため、より信頼性が高くなります)。

再帰的なgrepの場合、同等のトリックは以下を使用することです:

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

./*には、いくつかの望ましくない副作用があります。

cat ./*

ファイルごとにさらに2文字が追加されるため、引数+環境の最大サイズの制限に早く到達します。また、その./を出力で報告したくない場合もあります。お気に入り:

grep foo ./*

出力されます:

./a.txt: foobar

の代わりに:

a.txt: foobar

さらなる余談

1。コメントでの議論に続いて、ここでそれを拡張しなければならないような気がします。

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

上記の./で各ファイルの先頭をマークすると、各ファイル名の開始位置(./)と終了位置(次の./の前の改行または終了位置)を明確に識別できます出力の)。

つまり、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がたくさんありますが、yがありません。

GNU lsのような一部のツールは、出力が端末に送信されると、印刷不可能な文字を疑問符に置き換えます(ただし、(U + 2215)は印刷可能です)。 GNU 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

文字セットがASCIIであることをlsに伝えた後、???に変わった様子をご覧ください。

$ 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""x "を見つけることができます。印刷できない文字と非ASCII文字はすべてバックスラッシュシーケンスで表されます(バックスラッシュ自体は2つのバックスラッシュで表されます)。これは明確であることを意味します。これはGNU sedでした。すべてのPOSIX準拠のsed実装で同じである必要がありますが、一部の古い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が上に表示されたことはありません。これは完全に無関係ですissue with du -hs *これはファイル名とは関係ありませんが、注意する必要があります:duはディスク使用量を報告するため、すでにリストされているファイルへの他のリンクは報告しません(すべてのdu実装がそのように動作するわけではありませんが、ハードリンクがコマンドラインにリストされている場合)。

72

*./*は、どちらのファイルをリストするかという点で違いはありません。唯一の違いは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コマンドラインツールに、コマンドラインを介してファイル名を渡すことを保証したい場合があります。

では、なぜ./*を使用するのでしょうか。

@ Stephaneの回答が指摘 のように、Unixでファイルとディレクトリに名前を付けるときに有効な文字の性質により、さまざまなUnixコマンドに渡されると予期しない副作用が発生する危険なファイル名が作成される可能性がありますコマンドラインで。

したがって、./の使用は、さまざまなUnixコマンドに引数として渡されたときに、拡張されたファイル名がファイル名と見なされることを保証するために使用されます。

6
slm