在 bash 中,如何可以写入重定向输入的文件,并且可以防止它?

Lew*_*wis 23 bash io-redirection

我以为我对 bash 文件重定向有很好的处理能力,并且通常我会尽量避免“无用使用 cat ”,但是我在脚本中遇到了一些意外行为,我想了解为什么会发生这种情况。

在 bash 脚本中,我执行:

somecommand < file1 > file2
Run Code Online (Sandbox Code Playgroud)

我的期望是 file1 是安全的并且以只读方式打开。在实践中,我发现 file1 可以被覆盖。如何/为什么会发生这种情况,有没有办法在不诉诸于的情况下防止它cat

如果它像我想象的那样工作(该过程最终会得到一个直接的 rw 文件描述符?),那么以这种方式重定向文件似乎应该被认为是危险的,但我之前从未见过这种行为。

从我的案例中添加一些细节:有问题的命令是sops,它在后台做一些 GPG 的东西。提示符GPG密码有时被写入到用于输入文件,覆盖它。我使用的完整命令是:

sops --input-type json --output-type json -d /dev/stdin < ./secrets/file.json > ./secrets/file-decrypted.json
Run Code Online (Sandbox Code Playgroud)

从那以后我切换到了cat file1 | sops.. > file2,一切都按预期工作。我会说这是“对猫的无用使用” - 但它似乎不再那么无用了!


好像是gpg-agent没有运行的时候第一次提示的。

Sté*_*las 31

这是由于/dev/stdin(实际上/proc/self/fd/0)在 Linux(和 Cygwin,但通常不是其他系统)上实现的方式。

在 Linux 上打开/dev/stdin不像做 a dup(0),它只是重新打开与在 fd 0 上打开相同的文件。它不共享fd 0 所指的打开文件描述(使用只读模式),而是获取一个完全不相关的新打开文件描述,其模式与open().

因此,如果以 read+write 模式sops -d /dev/stdin打开/dev/stdin并且 fd 0 以只读方式打开 on /some/file/some/file则将以 read+write 方式打开。

实际上,cmd /dev/stdin < filecmd file < file. 你会发现这/dev/stdin只是一个符号链接¹到file

/tmp$ namei -l /dev/stdin < file
f: /dev/stdin
drwxr-xr-x root     root     /
drwxr-xr-x root     root     dev
lrwxrwxrwx root     root     stdin -> /proc/self/fd/0
drwxr-xr-x root     root       /
dr-xr-xr-x root     root       proc
lrwxrwxrwx root     root       self -> 73569
dr-xr-xr-x stephane stephane     73569
dr-x------ stephane stephane   fd
lr-x------ stephane stephane   0 -> /tmp/file
drwxr-xr-x root     root         /
drwxrwxrwt root     root         tmp
-rw-r--r-- stephane stephane     file
Run Code Online (Sandbox Code Playgroud)

它可能会变得更糟。如果它使用 O_TRUNCATE 打开,文件将被截断。如果 fd 0 指向管道的读取端并/dev/stdin以只写模式打开,您将获得管道的另一端。

但是使用:

cat file | cmd /dev/stdin
Run Code Online (Sandbox Code Playgroud)

将防止cmd覆盖,file因为所有人cmd都会看到管道。即使它确实以只写模式打开,它也无法返回到file,它只会到达管道的写入端,而读取端的唯一文件描述符将是cmd的标准输入。

其他操作系统没有这个问题,因为/dev/stdin在那里打开就像做一个dup(0),所以你得到相同的打开文件描述,如果你用不兼容的模式打开,open()系统调用就会失败。


¹从技术上讲,正如@user414777 在评论中所指出的,/proc/<pid>/fd/<fd>神奇的符号链接,例如它们可以到达普通符号链接无法到达的地方,但是当打开它们时,经过路径解析阶段,它们就像普通符号链接一样你只需打开目标文件

  • @terdon,`&lt; file` 不传递流或文件,它只是在 fd 0 上打开带有 O_RDONLY 的文件。应用程序只能从该 fd 中读取。问题仅在于那些特殊的 `/dev/stdin` / `/dev/fd`、`/proc/self/fd` 符号链接,它们在 Linux 上只是指向相应文件的符号链接,与打开相应文件的方式没有任何关系. (6认同)
  • @Lewis 你已经“信任”他们不阅读和发布你的`~/.ssh/id_rsa` 的程序。 (4认同)
  • 只是为了跟进,在我的情况下,嵌入式 mozilla-services/gogpagent 似乎是 [写入 /proc/self/fd/0](https://github.com/mozilla-services/gopgagent/blob/4d7ea76ff71aec34ae0b4e39ecee5c43f6815189/gpgagent .go#L147)。 (3认同)
  • 换句话说,您需要信任一个程序不使用/dev/stdin?因此,养成以这种方式重定向文件进行输入的习惯确实存在潜在危险吗? (2认同)
  • @KamilMaciorowski 是的,但不知何故,这感觉不同。我没有意识到我的数据在这样传递时处于危险之中:) (2认同)
  • 它们不仅仅是“符号链接”。它们是“魔法”符号链接,即使它们的目标未解析或解析为无法访问的路径,也可以打开它们。 (2认同)