Answers:
$ 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变量设置a
为b.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
的很多,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
了解如何∕
转身???
后,我们告诉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
虽然列出了硬链接后,并非所有实现都具有这种行为)在命令行上)。
[a-z0-9.+-]
。
/tmp
)或有很多昂贵的汽车的区域($HOME
),更糟糕的是去问与答站点,并说最好不要在没有指定条件的情况下(在上锁的车库,剧本中)上锁汽车您编写的代码只能在未连接任何网络或可移动存储的计算机上自己运行...)
"b "
或"a\bb"
)会欺骗终端上的用户,但不会欺骗解析的输出的脚本du ./*
。我可能应该对此添加注释。明天会做。请注意,较早时候,我的意思是一般意义上的特权,而不是root
(尽管root
当然更适用)。允许换行符,忽略它们是一个错误。错误有被利用的习惯。您必须逐案评估风险。良好的编码习惯可以在许多情况下避免出现此问题。当然,对于SE,我们应该提高认识。
在*
和./*
列出的文件之间没有区别。唯一的区别是第二种形式,每个文件在文件名./
前都有一个斜杠前缀,通常表示当前目录。
请记住,该.
目录是当前目录的简写形式。
$ 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命令时被视为文件名。