“du -sh *”和“du -sh ./*”有什么区别?

Bis*_*ath 29 shell bash options filenames wildcards

du -sh *和 和有du -sh ./*什么区别?

注意:我感兴趣的是*./*部分。

Sté*_*las 78

$ touch ./-c $'a\n12\tb' foo
$ du -hs *
0个
12 乙
0 富
共 0 个

正如您所看到的,该-c文件被视为一个选项du并且没有报告(并且您看到该total行是因为du -c)。此外,被调用的文件a\n12\tb使我们认为存在名为a和 的文件b

$ touch ./-c $'a\n12\tb' foo
$ du -hs *
0       a
12      b
0       foo
0       total

这样更好。至少这次-c不作为一种选择。

$ du -hs -- *
0       a
12      b
0       -c
0       foo
Run Code Online (Sandbox Code Playgroud)

那更好。所述./前缀防止-c正在采取从作为一个选项和不存在的./之前b的输出表明没有b在那里的文件,但有一个与一个新行字符一个文件(但参见下文1上进一步离题)。

./在可能的情况下使用前缀是一种很好的做法,如果没有,对于任意数据,您应该始终使用:

$ du -hs ./*
0       ./a
12      b
0       ./-c
0       ./foo
Run Code Online (Sandbox Code Playgroud)

或者:

cmd -- "$var"
Run Code Online (Sandbox Code Playgroud)

如果cmd不支持--标记选项的结尾,则应将其作为错误报告给其作者(除非它是由选择并记录为 for 的情况echo除外)。

在某些情况下,./*解决了--没有解决的问题。例如:

cmd -- $patterns
Run Code Online (Sandbox Code Playgroud)

如果a=b.txt在当前目录中调用了一个文件,则失败(将 awk 变量设置ab.txt而不是告诉它处理该文件)。

awk -f file.awk -- *
Run Code Online (Sandbox Code Playgroud)

没有问题,因为./a它不是有效的 awk 变量名,因此./a=b.txt不被视为变量赋值。

awk -f file.awk ./*
Run Code Online (Sandbox Code Playgroud)

如果-在当前目录中调用了一个文件,则失败,因为它告诉cat从其 stdin 读取(-对于大多数文本处理实用程序和cd/ 而言是特殊的pushd)。

cat -- * | wc -l
Run Code Online (Sandbox Code Playgroud)

还可以,因为./-cat.

像:

cat ./* | wc -l
Run Code Online (Sandbox Code Playgroud)

计算包含的文件数foo是错误的,因为它假定文件名不包含换行符(wc -l计算换行符、grep每个文件的输出以及文件名本身中的那些)。你应该使用:

grep -l -- foo *.txt | wc -l
Run Code Online (Sandbox Code Playgroud)

(计算/字符数更可靠,因为每个文件名只能有一个)。

对于 recursive grep,等效的技巧是使用:

grep -l foo ./*.txt | grep -c /
Run Code Online (Sandbox Code Playgroud)

./* 虽然可能有一些不需要的副作用。

grep -rl foo .//. | grep -c //
Run Code Online (Sandbox Code Playgroud)

每个文件再增加两个字符,所以会让你更快地达到参数+环境的最大大小的限制。有时您不希望./在输出中报告。喜欢:

cat ./*
Run Code Online (Sandbox Code Playgroud)

会输出:

grep foo ./*
Run Code Online (Sandbox Code Playgroud)

代替:

./a.txt: foobar
Run Code Online (Sandbox Code Playgroud)

进一步的题外话

1 . 在评论中的讨论之后,我觉得我必须在这里对此进行扩展。

a.txt: foobar
Run Code Online (Sandbox Code Playgroud)

上面,./标记每个文件的开头意味着我们可以清楚地识别每个文件名的开始位置(在./)和结束位置(在下一个./或输出结束之前的换行符处)。

这意味着与 ) 的输出du ./*相反,du -- *可以可靠地解析 )的输出,尽管在脚本中并不那么容易。

但是,当输出到终端时,文件名还有很多方法可以欺骗您:

  • 控制字符、转义序列会影响事物的显示方式。例如,\r将光标移动到行首,\b向后、\e[C向前移动光标(在大多数终端中)......

  • 许多字符在终端上是不可见的,从最明显的一个开始:空格字符。

  • 有些 Unicode 字符看起来与大多数字体中的斜杠相同

    $ du -hs ./*
    0       ./a
    12      b
    0       ./-c
    0       ./foo
    
    Run Code Online (Sandbox Code Playgroud)

(看看它在浏览器中的运行情况)。

一个例子:

 $ printf '\u002f \u2044 \u2215 \u2571 \u29F8\n'
 / ? ? ? ?
Run Code Online (Sandbox Code Playgroud)

很多,xy不见了。

当输出到终端时,像GNUls 这样的一些工具会用问号替换不可打印的字符(注意?(U+2215) 是可打印的)。GNUdu没有。

有一些方法可以让它们暴露自己:

$ 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
Run Code Online (Sandbox Code Playgroud)

看看我们告诉我们我们的字符集是 ASCII之后如何?转向。???ls

$ 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
Run Code Online (Sandbox Code Playgroud)

$标记行的结尾,因此我们可以发现"x"vs "x ",所有不可打印的字符和非 ASCII 字符都由反斜杠序列表示(反斜杠本身将用两个反斜杠表示),这意味着它是明确的。那是 GNU sed,它在所有符合 POSIX 的sed实现中应该是相同的,但请注意,一些旧的sed实现几乎没有帮助。

$ 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$
Run Code Online (Sandbox Code Playgroud)

(不标准但很常见,也cat -A有一些实现)。那一个是有帮助的,并且使用不同的表示,但是不明确的("^I"<TAB>显示在同一例如)。

$ du -hs ./* | cat -vte
0^I./x$
0^I./x $
0^I./x$
0^I.M-bM-^HM-^Ux$
Run Code Online (Sandbox Code Playgroud)

那个是标准和明确的(并且从实现到实现一致)但不那么容易阅读。

你会注意到y上面从来没有出现过。这是一个完全不相关的问题du -hs *与文件名无关,但应该注意:因为du报告磁盘使用情况,它不会报告指向已列出文件的其他链接(du尽管在列出硬链接时,并非所有实现都如此在命令行上)。

  • @BlacklightShining,偷车很糟糕,但让你的车解锁(忽略换行符)是很糟糕的,尤其是当它是一辆昂贵的汽车时(脚本作为特权用户运行,在具有敏感数据的服务器上......)或者当你把它停在一个粗糙的区域(`/tmp`)或一个有很多昂贵汽车的区域(`$HOME`),更糟糕的是去一个问答网站并说不要在没有指定的情况下锁车总是好的条件(在上锁的车库中,您自己编写的脚本仅在未连接到任何网络或可移动存储的机器上运行...) (6认同)
  • @BlacklightShining,是的,虽然那个(如`"b"`或`"a\bb"`)会在终端上欺骗用户,但不会欺骗解析`du ./*`的输出的脚本。我可能应该添加一个注释。明天做。请注意,之前我指的是一般意义上的 _privileged_,而不是“root”(当然,当然更适用于“root”)。允许换行,忽略它们是一个错误。漏洞有被利用的习惯。您必须根据具体情况衡量风险。在很多情况下,良好的编码习惯可以避免这些问题。当然,在 SE 上,我们应该提高认识。 (2认同)

slm*_*slm 7

a*和之间./*在列出哪些文件方面没有区别。唯一的区别是第二种形式,每个文件./前面都有一个点斜线前缀,这通常表示当前目录。

请记住,.目录是当前目录的速记符号。

$ 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
Run Code Online (Sandbox Code Playgroud)

您可以通过使用echo查看 shell 将它们扩展为什么来说服自己这两个列表本质上是相同的。

$ echo *
$ echo ./*
Run Code Online (Sandbox Code Playgroud)

这 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
Run Code Online (Sandbox Code Playgroud)

现在,当我们使用上述echo命令时,我们会看到以下输出:

$ echo *
file1 file2 file3 file4 file5
$ echo ./*
./file1 ./file2 ./file3 ./file4 ./file5
Run Code Online (Sandbox Code Playgroud)

这种差异似乎没有必要,但在某些情况下,您希望向各种 Unix 命令行工具保证您通过命令行将文件名传递给它们,仅此而已!

那么为什么要使用./*?

正如@Stephane 的回答所指出的那样,由于在 Unix 中命名文件和目录时哪些字符是合法的,因此可以构造危险的文件名,当它们在命令行中传递给各种 Unix 命令时会产生意想不到的副作用。

经常使用./will 来帮助保证扩展的文件名在作为参数传递给各种 Unix 命令时被视为文件名。