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 变量设置a
为b.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)
很多,x
但y
不见了。
当输出到终端时,像GNU
ls 这样的一些工具会用问号替换不可打印的字符(注意?
(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
尽管在列出硬链接时,并非所有实现都如此在命令行上)。
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 命令时被视为文件名。