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


Answers:


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

如您所见,该-c文件被视为选项,du并且未报告(并且您看到此total行是因为du -c)。此外,称为的文件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上进一步离题)。

最好./在可能的情况下使用前缀,否则,对于任何数据,都应始终使用:

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 /

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

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

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

./* 可能会有一些不良的副作用。

cat ./*

每个文件又增加了两个字符,因此可以使您更快地达到arguments + environment的最大大小限制。有时您不希望./在输出中将其报告。喜欢:

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

的很多,xy丢失了。

当输出到达终端时,诸如GNUls之类的某些工具会用问号替换不可打印的字符(请注意,虽然(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

了解如何转身???后,我们告诉ls我们的字符集是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$

$标记行的结尾,因此我们可以发现"x"vs "x ",所有不可打印字符和非ASCII字符均由反斜杠序列表示(反斜杠本身将由两个反斜杠表示),这意味着它是明确的。那就是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上面从未出现过。这是一个完全不相关的问题du -hs *与文件名无关,但应注意:由于du报告磁盘使用情况,因此不会报告指向已列出文件的其他链接(du虽然列出了硬链接后,并非所有实现都具有这种行为)在命令行上)。


+1,尼斯而彻底(据我所知^^)。我特别喜欢“ grep -c /”的优势。同样值得注意的是:“ ./*”优于“ *”的优点出现在Unix FAQ的(很多)很好的答案之一中(可能在faqs.org.iirc上,这是关于以-m开头的rm-ing文件的问题)一种 ”-”)。
奥利维尔·杜拉克

… 在名称中包含换行符和制表符的文件是坏的做法?我知道尝试将名称限制为[a-z0-9.+-]
Blacklight Shining

5
@BlacklightShining,这是非常糟糕的盗车型,但它的离开你的车解锁(忽略换行),特别是当它是一个昂贵的汽车(脚本运行的特权用户敏感数据的服务器上的...)或当您将其停放在崎area不平的区域(/tmp)或有很多昂贵的汽车的区域($HOME),更糟糕的是去问与答站点,并说最好不要在没有指定条件的情况下(在上锁的车库,剧本中)上锁汽车您编写的代码只能在未连接任何网络或可移动存储的计算机上自己运行...)
StéphaneChazelas 2014年

1
@BlacklightShining,换行符很不寻常,但是如今嵌入空间非常普遍,尤其是对于通过GUI创建的文件。
Alexis 2014年

2
@BlacklightShining,是的,尽管这样(如"b ""a\bb")会欺骗终端上的用户,但不会欺骗解析的输出的脚本du ./*。我可能应该对此添加注释。明天会做。请注意,较早时候,我的意思是一般意义上的特权,而不是root(尽管root当然更适用)。允许换行符,忽略它们是一个错误。错误有被利用的习惯。您必须逐案评估风险。良好的编码习惯可以在许多情况下避免出现此问题。当然,对于SE,我们应该提高认识。
斯特凡Chazelas

6

*./*列出的文件之间没有区别。唯一的区别是第二种形式,每个文件在文件名./前都有一个斜杠前缀,通常表示当前目录。

请记住,该.目录是当前目录的简写形式。

$ 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

您可以通过echo查看外壳将它们扩展到什么来使自己确信这两个列表本质上是相同的。

$ echo *
$ echo ./*

这两个命令将列出当前目录中的所有文件。

例子

我们可以这样制作一些假数据:

$ 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命令时被视为文件名。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.