有没有永远不存在的文件?

Ctr*_*tF2 63 files

沿着/dev/null(指向空源/接收器文件的路径),是否存在至少在 Linux 上永远不会指向有效文件的路径?这主要是为了测试我正在编写的一些脚本,我不想删除或移动不属于脚本的文件(如果它存在)。

ter*_*don 104

作为替代方案,我建议您的脚本创建一个临时目录,然后在其中查找文件名。这样,您就可以 100% 确定该文件不存在,并且您拥有完全控制权并且可以轻松地自行清理。就像是:

dir=$(mktemp -d)
if [ -e "$dir"/somefile ]; then
    echo "Something is seriously wrong here, '$dir/somefile' exists!"
fi
rmdir "$dir"
Run Code Online (Sandbox Code Playgroud)

您可以用任何语言编写等效的代码,绝大多数(所有?)高级语言都有一些专用工具来处理创建和删除临时目录。与尝试猜测不应该存在的文件名相比,这似乎是一种更安全、更简洁的方法。

  • (注意:如果您在检查之前对在新临时目录中创建文件的恶意其他进程感到偏执:1. 如果您自己的 UID 或 root 正在攻击您,您可能会遇到更大的问题,2. 查看修订历史和聊聊疯狂的极端想法,以减少这种可能性。) (3认同)

Kam*_*ski 86

/dev/null/foo不能存在,除非/dev/null是一个目录。

POSIX 要求/dev/null是“空数据源和无限数据接收器”。我不确定是否完全不可能拥有具有这些特征的目录。尽管如此,我认为假设/dev/null它不是 *nix 中的目录是很安全的。

请注意,如果您尝试打开,/dev/null/foo那么您将获得ENOTDIR(不是目录),而不是ENOENT(没有这样的文件或目录)。对于您的测试目的,这可能会或可能不会被接受。

  • @ThorbjørnRavnAndersen 是的。但是,如果您是 root,那么您也可以破坏 `mktemp`,从而使 [this other answer](https://unix.stackexchange.com/a/643527) 变得不可靠。需要明确的是:我并不是要诋毁另一个答案。事实上`mktemp -d`在我的(现已删除)评论中。我当时评论了,没有回答,因为 IMO `mktemp -d` 不是“沿着 `/dev/null` 的行”。大约在同一时间,特登做出了一个公正而公正的回答。OTOH 我认为 `/dev/null/foo` 是“沿线”,因此我的回答。好吧,我想我的吹毛求疵浪费了我写出最佳答案的机会。 (11认同)
  • *`/dev/null/foo` 不能存在,除非`/dev/null` 是一个目录。* 它可能发生。在以 root 身份运行的拙劣的 shell 脚本对 `/dev/null` 做了难以形容的事情后,我不得不修复系统。所以是的,我已经将 `/dev/null` 看作是一个普通文件和一个目录。 (6认同)
  • 如果您是 root 用户,过去可以删除 /dev/null 特殊设备,然后将其重新创建为目录。偶然做了一次类似的事情 - 花了一段时间才弄清楚发生了什么。 (4认同)
  • 这并不是要贬低您的答案,而是要让未来的读者知道当事情表现异常时可能会发生的神秘罪魁祸首。另一个,可以是隐藏在作为挂载点的目录中的文件。该磁盘空间很难找到。 (3认同)
  • 如果你不需要`ENOENT`,你也可以很容易地通过使用一个很长的名字来获得`ENAMETOOLONG`。但是`/dev/null/foo` 更短,即使在有巨大限制或没有限制的系统上也能工作。 (3认同)
  • @Ruslan 不。它一直是一个非常好的文件系统。问题是有人编写了一个 shell 脚本来收集一些输出,他们很聪明地删除了输出文件,然后创建一个具有该名称的目录并将文件放入该目录中,或者创建一个新的输出文件。然后其他人以 root 身份运行该脚本并使用 `/dev/null` 作为输出文件。告别`/dev/null`... (2认同)

ilk*_*chu 33

从任何密码学手册中获取一页,如果您生成一个足够大的随机字符串,它只会以微不足道的概率存在于任何给定系统中,以至于可以忽略命中的可能性。

例如,假设你有一个工作/dev/urandom(你应该),这样的事情会f基于一个 128 位随机数生成一个有效的文件名:

f=/$(head -c 16 /dev/urandom |base64 |tr / ,)
Run Code Online (Sandbox Code Playgroud)

输出类似于/B90sYd,aNrcw7d7Itcb8fQ==. (前导斜杠是固定的,故意使其成为绝对路径。尾随==也是固定的,并且由于 Base64 填充。它们可以被忽略。)

以 1 万亿/秒的速度生成随机文件名的系统需要数万亿年才能生成由脚本生成的文件名。请注意,任何碰撞都不够,因此生日攻击不适用。这与暴力破解 128 位对称密钥基本相同。

另请参阅例如: 暴力破解 AES-128 密钥需要多长时间?

请注意,这需要一个有效的/dev/urandom. 如果有人用包含一些已知字符串的静态文件替换它,它将不起作用,例如三个字节\x86\x89\x9e,当 Base64 编码时,产生字符串home;或者如果您处于例如不可用的chrooted 上下文中/dev/urandom。此外,例如,没有真正的方法来初始化系统的 RNG 的嵌入式系统可能会面临问题。不要在这种情况下使用它,但在这种情况下也不要生成任何加密密钥


作为替代方案,您可以使用空字符串。至少在 Linux 上,尝试将其用作文件名只会出现错误:

$ cat ""
cat: '': No such file or directory
$ touch ""
touch: cannot touch '': No such file or directory
Run Code Online (Sandbox Code Playgroud)

ENOENT顺便说一句,我发现它给出的错误 ( ) 有点有趣。有人可能认为它会说名称无效,而不是它不存在。)

请注意,如果将其放入变量中,则在扩展它时确实需要记住引号!例如f=; cat $f,只会从标准输入读取。

$ f=
$ cat "$f"
cat: '': No such file or directory
$ touch "$f"
touch: cannot touch '': No such file or directory
Run Code Online (Sandbox Code Playgroud)

但是,如果您cd ""在 shell 中这样做,它只会更改到当前目录。POSIX说的是“如果给定的路径]是一个空字符串,其结果是不确定的。” . 我尝试过的所有 shell 都在chdir()调用中明确使用当前路径,例如:

/tmp$ strace -etrace=chdir zsh -c 'cd ""'
chdir("/tmp")                           = 0
+++ exited with 0 +++
/tmp$
Run Code Online (Sandbox Code Playgroud)

我根据之前(现已删除)的答案得到了这个想法。这可能是也可能不是系统特定的,我只在 Linux 上尝试过。买者自负。


此外,如评论中所述,如果您不关心得到的确切错误,或者使用甚至不告诉您的内容,例如[ -f ... ][ -e ... ]在 shell 中,您可以创建一个过长的文件名。

在几乎所有文件系统上,单个文件的最大长度为 255 或更少(请参阅Wikipedia上文件系统比较中的表格)。完整路径可以更长,但 256 字节的单个文件名是不可能的,并给出ENAMETOOLONG

$ f=$(printf %256s x | tr ' ' x)
$ touch "$f"
touch: cannot touch 'xxx...xxx': File name too long
Run Code Online (Sandbox Code Playgroud)

但是在if [ -e "$f" ]; then ...我尝试过的所有 shell 中都可以正常工作(并且测试失败)。

POSIX 定义-e“如果路径名无法解析,则为 False ”,但没有明确提及诊断。因此,也许某些实现在某些情况下可能会出错,我不确定。请告诉您是否发现了这种情况.)

(维基百科中的表格确实提到了两个具有更高每个文件长度的 Linux 文件系统,但我怀疑它们现在是否被广泛使用,而且我也知道 Linux 通常有 255 字节的限制,无论文件系统如何。)

  • @ThorbjørnRavnAndersen,是的......好吧,如果你曾经使用过例如 git,你就依赖于没有两个对象生成相同的 SHA-1 哈希。(SHA-1 是 160 位,但由于它可以是 _any_ 对,所以生日攻击适用,并且您只需要大约 2^80 个对象就有 50% 的碰撞机会。)几乎所有现代密码学都类似。从理论上讲,您可能会很不走运,以至于有人在第一次尝试时就猜到了您的密码。(或您的 GPG 密钥,或其他任何东西。) (9认同)
  • @ThorbjørnRavnAndersen,但实际上,这不仅仅是通常意义上的“有一天倒霉”;更像是被闪电击中、被鲨鱼咬伤、车祸幸存、中彩票的顺序,都在同一天。然后还有一些。数字很​​大。我的意思是,令人难以置信的大。我链接到的那篇crypto.SE帖子将其与_宇宙的年龄_进行了比较,并且仍然需要再添加两个零。 (5认同)
  • @ThorbjørnRavnAndersen 还有一种可能性大于零,您的计算机会自发地长出一只手臂并开始扇人耳光。不过,我猜你不认为这在现实中是可能的。同样,随机生成的具有 128 位熵的文件名与系统上已存在的文件名巧合地匹配,这实际上是不可能的。 (3认同)
  • 我认为无论如何总是在扩展周围包含引号是一种很好的做法。 (2认同)
  • @ThorbjørnRavnAndersen 你比你想象的更依赖“很可能永远不会发生”。密码学(几乎所有密码学)都基于永远不可猜测的随机数。因此,您曾经做过的任何银行业务都以“很可能永远不会发生”为担保 (2认同)
  • @ThorbjørnRavnAndersen:“百万分之一”比“千万分之一”(2 ^ 80)要大得多,令人难以置信。这就是重点。 (2认同)
  • @ThorbjørnRavnAndersen,一百万(1 000 000)是 10^6,大约 2^20。那么,2^128 大约是 10^38。有整整 32 个数量级的差异。相比之下,这是 [相同的比例差异](https://en.wikipedia.org/wiki/Orders_of_magnitude_(length)) 红细胞的大小 (~10^-6 m) 和直径之间的差异可见宇宙(~10^26 m)。所以是的,百万分之一是下周二,或者如果你再努力一点,下一秒。一次在 10^38 永远不会。 (2认同)
  • 现在,如果有人想继续争论统计数据和数字尺度,请把它带到别处。如果必须的话,请在途中投票,但我有点厌倦了该主题的 ping。 (2认同)

DrS*_*don 17

由于进程 ID 永远不会为负,/proc/-1因此永远不会存在。

即使您的 Unix 变体不支持 procfs(或未安装),这也有效!


使用文件描述符的类似方法: /dev/fd/-1

我想 Unix 的某些变体理论上可能允许 -1 的文件描述符,但这会破坏很多现有代码。


自动生成的文件系统可能还有许多其他这样的可能性。

  • 这可能假设正在使用 Linux 系统。其他具有`/proc` 文件层次结构的Unices 可能具有`/proc/-1` 名称。 (5认同)
  • 我会选择类似`/proc/-123`甚至`/proc/xyz-non-existent`;正如@Kusalananda 所说,对于自动生成的 FS 中可能的 Unix 来说,`-1` 听起来并不那么疯狂。一个只能手动创建的奇怪文件名,在大多数系统上无法手动创建的目录中,是一个更安全的选择。 (2认同)

Bri*_*ake 13

terdon已经给出了很好的答案:使用mktemp -d. 这篇文章以更基本的方式看待这个问题,并解释了为什么这个命令通常是最好的答案。

没有永远不存在的恒定路径。但是根据您关于测试脚本的句子,如果脚本可以自行生成,则听起来变量路径也一样好。

生成这种路径的唯一方法是不断生成路径,直到生成的路径不存在。不幸的是,这可能会导致竞争条件您检查文件是否存在之后,其他一些程序可能会创建该文件,但您执行取决于该文件不存在的任何操作之前

避免这种竞争条件的最佳方法是使用其他程序应该避免的路径,例如/tmp. 不幸的是,它/tmp往往是全局可写的,所以现在你有一个更大的问题:其他一些用户可能会创建该文件。

您真正想要的是其他程序应该避免其他用户无权访问的临时目录。如果目录为空则更好,因此您可以使用该目录下的任何路径。

mktemp -d创建一个满足上一段中所有标准的目录,同时保护自己免受自己的竞争条件。完成后,您可以使用rmdir删除目录:

dir="$(mktemp -d)"
# Use "$dir"/foo as a nonexistent path.
rmdir "$dir"
Run Code Online (Sandbox Code Playgroud)


Way*_*rad 8

我喜欢保持简单。我没有使用不可能存在的路径,而是使用我永远不会创建的路径,我也无法想象任何人会创建。

我需要不存在路径的测试使用类似/thisfiledoesnotexist. 我认为这是合理的,因为:

  • 文件名是自我记录的。
  • 创建此文件需要 root 权限,因此不太可能意外创建。
  • 我永远不会创造它。
  • 我无法想象为什么其他人会这样做。Linux 系统管理员喜欢保持根目录干净

如果您真的很偏执,那么您的测试工具可以首先测试该文件不存在,如果存在则失败。只有当文件不存在时,它才会继续进行测试。