空字符串何时表示当前目录?


8

在脚本中,我用于find收集当前目录中的一些文件,如下所示:

$ find . -name "*.h"
./foo.h

现在,我希望它只是输出foo.h,不带./前缀。我认为空字符串""表示Shell命令中的当前目录。但这给出了:

$ find "" -name "*.h"
find: ftsopen: No such file or directory

所以我错了。现在我的问题是何时/如何/在哪里/ ..“空字符串(?)”表示需要文件名或路径名的命令中的当前目录吗?有一个整洁而有启发性的解释吗?

附带的一个问题是,上面的查找是否可以简单地解决,而无需字符串操作la ${parameter#word}cutor sed


1
find具有-printf参数来控制如何显示结果。find . -name '*.h' -printf '%P\n'将删除./前缀。看看能做什么find . -name '*.h' -print
Valentin Bajrami 2014年

谢谢!太糟糕了,我的find版本不支持该-printf选项。FWIW,strings根据我的发现会给出@(#)PROGRAM:find PROJECT:shell_cmds-175?!?
ph 2014年

5
@phs使得必须始终提及您的操作系统非常重要。
terdon

Answers:


11

很久以前(在 第7版32V4.2BSD4.3BSD)中,在系统调用级别,零长度路径名表示当前工作目录(用于查找时;尝试创建或删除文件时不允许使用该路径名)文件或目录)。在 System III中,在任何情况下都使用零长度路径名是一个错误,并且POSIX标准对此有关于路径名解析的说明:

空路径名将无法成功解析。


3
$ PATH,$ MANPATH,$ LD_LIBRARY_PATH(以及其他但不一定都是$ * PATH的一个)中的空字符串表示当前目录。dir/大多是一样的dir/.
斯特凡Chazelas

4

您可以使用:

find * -name "*.h"

请注意,当前目录中名称以开头的.文件将被省略,而名称以开头的文件-将被解释为选项,find并造成严重破坏,因此这通常与等效find . …

缺少/作为文件名一部分的以“ ” 结尾的字符串表示当前目录,但这并不意味着当前目录由空字符串表示(这可以说是与缺少字符串不同, (虽然在打印时看起来可能一样)。


4

通常,无论是Shell命令还是系统调用中,空字符串都不表示当前目录。它在某些较旧的系统上运行,但在POSIX兼容的系统上却没有

有时,您会在传递空字符串时发现使用当前目录的程序,并且该程序需要目录名。有时这是故意的,有时会给定的字符串不是以斜杠开头时,在当前目录的绝对路径之前加上一个副作用。

对你来说最好的是离开./。它没有任何危害。

如果文件列表用于

find . … | sed 's!^\./!!'

请注意,这会破坏某些包含换行符的文件名。通常,这对于人类的消费而言不是问题,并且find由于其模棱两可,因此其输出也不适合程序消费。如果您使用的-print0是适合程序使用的,那么您可能根本不关心./前缀。

您可以使用find * …代替find . …,但请注意,find *它存在许多缺陷,通常不适合使用:

  • . 被省略。
  • .省略所有点文件(名称以或..` 开头的文件)。
  • 如果在当前目录名称以一个文件名-(或称为文件!(......),它会被解释为一个选项,或断言find

第一点与您的过滤器是否排除当前目录无关。第二点,您可以使用模式..?* .[!.]* *来匹配当前目录中的所有文件,但是您需要检查每个模式是否至少匹配一个文件,如果不匹配则将其忽略。这是可能的,但是非常麻烦。最后一点是塞子。因此find *可能适用于quickie命令行,但不要在脚本中使用它。

一种替代方法是使用外壳程序的递归遍历功能,例如

printf '%s\n' **/*.h

这需要通过shopt -s globstarbash和set -o globstarksh93 来激活,并且在基本的POSIX shell(例如破折号)中不存在。默认情况下,不会遍历点文件;要包含它们,首先要使blob shopt -s dotglobFIGNORE='@(.|..)'ksh93中的glob不能忽略点文件。另外,如果没有匹配项,则此命令将打印出图案;shopt -s nullglob在bash中运行以打印空行,并~(N)**/*.h在ksh中使用该模式。

在zsh中,默认情况下启用递归glob。使用glob限定词D包括点文件,N如果没有匹配项,则打印空行(默认情况下,如果模式与任何文件都不匹配,则zsh会引发错误)。您可以使用printf上面的或

print -rl -- **/*.h(DN)

感谢所有这些技巧。大多数缺陷find *对我来说都是新的,有些令人恐惧!
2014年

2

我想不出任何示例,其中空字符串表示当前目录.。您可能会想到类似这样的调用ls,但这是因为ls如果未提供任何参数,则假定当前目录,并且实际上它不会采用空字符串:

ulmi@silberfisch:~$ ls ""
ls: cannot access : No such file or directory

3
空字符串表示当前目录的一个示例是作为PATH组件。
2014年

0

您可以使用多种技巧来获取find的输出而无需使用前导./

  1. 使用find的-printf选项,并告诉它仅打印%f。见man find

    %f文件名,其中删除了所有前导目录(仅最后一个元素)。

    例如:

    find . -name "*.h" -printf "%f\n"
    
  2. 解析输出:

    find . -name "*.h" | sed 's#^./##'
    
  3. @Anthon的把戏

    find * -name "*.h"
    

至于您的其他问题,空字符串从不表示当前目录。只是各种程序将当前目录作为默认目录,因此当您不带参数运行它们时,它们将在当前目录上运行。


假设使用GNU find。或者使用%P仅剥离./
斯特凡Chazelas

1
find * -name '*.h'会找到foo/.bar.h,但不在.bar.h当前目录中。
斯特凡Chazelas

0

如果文件在当前目录中,则可以使用ls

$ ls *.sh

如果您还想要子目录中的文件,并且只需要文件名,则可以执行以下操作:

$ find . -name '*.h' -exec basename {} \;

有些命令将缺少字符串作为当前目录,但是基本上是因为如果未输入任何内容,它们将默认获得当前目录。该.总表示当前目录(和..父目录)


OP仅需要./删除,而您的find解决方案将删除所有目录组件
artm 2014年
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.