在一个脚本中,我用来find
收集当前目录中的一些文件,如
$ find . -name "*.h"
./foo.h
现在我希望它只输出foo.h
,没有./
前缀。我认为空字符串""
表示 shell 命令中的当前目录。但这给出了:
$ find "" -name "*.h"
find: ftsopen: No such file or directory
所以我错了。现在我的问题是何时/如何/在哪里/..“空字符串(?)”是否表示需要文件名或路径名的命令中的当前目录?有简洁且有启发性的解释吗?
一个附带问题是上面的 find 挑剔是否可以简单地解决,而不需要字符串操作 la ${parameter#word}
or cut
or sed
?
答案1
答案2
您可以使用:
find * -name "*.h"
请注意,当前目录中名称以 开头的文件.
将被忽略,并且名称以 开头的文件-
将被解释为选项find
并造成严重破坏,因此这不是一般等价于find . …
.
/
文件名中缺少以“”结尾的字符串暗示当前目录,但这并不意味着当前目录是表示通过空字符串(可以说这与缺少字符串不同,尽管打印时看起来可能相同)。
答案3
一般来说,空字符串并不表示当前目录,无论是对于 shell 命令还是系统调用都是如此。它在一些较旧的系统上有效,但在符合 POSIX 的系统上无效。
有时,当您传递空字符串并且程序需要目录名称时,您会发现程序使用当前目录。这有时是故意的,有时是当给定字符串不以斜杠开头时在当前目录的绝对路径前面添加的副作用。
对你来说最好的事情就是离开./
。它没有任何害处。
如果文件列表是
find . … | sed 's!^\./!!'
请注意,这会破坏一些包含换行符的文件名。这对于人类消费来说通常不是问题,并且 的输出find
不适合程序消费,因为它是不明确的。如果您使用的-print0
是适合程序使用的 ,那么您可能并不关心前缀./
。
您可以使用find * …
代替find . …
,但请注意,它find *
有许多缺陷,通常不适合:
.
被省略。- 所有点文件(名称以 或 ..` 开头的文件
.
)都将被省略。 - 如果当前目录中有一个以 开头的文件名(或名为or ...
-
的文件),它将被 解释为选项或谓词。!
(
find
如果您的过滤器排除当前目录,第一点并不重要。对于第二点,您可以使用模式..?* .[!.]* *
来匹配当前目录中的所有文件,但您需要检查每个模式是否至少匹配一个文件,如果不匹配则忽略它。这是可能的,但非常麻烦。最后一点是一个塞子。因此find *
可能适合快速命令行使用,但不要在脚本中使用它。
另一种方法是使用 shell 的递归通配工具,例如
printf '%s\n' **/*.h
shopt -s globstar
这需要通过bash 和ksh93来激活set -o globstar
,并且在基本的 POSIX shell(例如 dash)中不存在。默认情况下不会遍历dot文件;要包含它们,首先使 globbing 不忽略shopt -s dotglob
bash 或FIGNORE='@(.|..)'
ksh93 中的点文件。此外,如果没有匹配项,则此命令将打印模式;在 bash 中运行shopt -s nullglob
以打印空行,并~(N)**/*.h
在 ksh 中使用该模式。
在 zsh 中,默认情况下启用递归通配符。使用 glob 限定符D
来包含点文件,N
如果没有匹配则打印空行(默认情况下,如果模式与任何文件都不匹配,zsh 会抛出错误)。您可以printf
像上面那样使用或
print -rl -- **/*.h(DN)
答案4
您可以使用多种技巧来获取没有前导的 find 的输出./
:
使用 find 的
-printf
选项并告诉它仅打印%f
。看man find
:%f 删除所有前导目录的文件名(仅最后一个元素)。
例如:
find . -name "*.h" -printf "%f\n"
解析输出:
find . -name "*.h" | sed 's#^./##'
-
find * -name "*.h"
至于您的其他问题,空字符串从不表示当前目录。只是各种程序都将当前目录作为默认目录,因此当您不带参数运行它们时,它们将在当前目录上运行。