我该如何在编辑时将文件保留在内存中?

7
简短版本:echo "testing" | vim - | grep "good"
这不起作用,因为vim无法输出到管道。它会显示:“Vim: Warning: Output is not to a terminal”。有什么方法可以解决这个问题吗?同时支持多个编辑器也很好。
我尝试过使用命名管道,但vim无法打开它们。
长版本:echo $passw | gpg -q -d --passphrase-fd 0 $filename | vim - | “以某种方式使用$passw加密它并将其存储回$filename”。
我正在尝试编辑一个gpg加密的文件,但希望在任何时候都不必在磁盘上保留解密后的文件。
完整脚本在此处:https://github.com/ptarjan/viencrypt

1
在您的密码短语上执行路径名扩展和单词拆分听起来像一个非常糟糕的想法。学会引用:http://mywiki.wooledge.org/BashGuide/Practices/Quoting -- 引用每个参数扩展("$passw","$filename",..)。 - lhunath
1
@lhunath,@Paul Tarjan:或者直接迁移到zsh。当你使用command $var时,默认情况下不会执行路径名扩展或单词分割:这里完全等同于command“$var”,除非你设置了一些奇怪的(对于zsh用户来说)选项。 - ZyX
@Paul,脚本还可以用吗?链接已经失效了... - Simon
@Simon 更新了链接,感谢您指出。 - Paul Tarjan
sh ~/viencrypt.sh mypasswords.gpg # 在vim中打开文件,工作正常 - zzapper
8个回答

8

请注意,默认情况下Vim会在磁盘上创建交换文件。使用以下命令启动vim以避免这种情况:

vim "+set noswapfile"

太棒了。已经添加到脚本中了。 - Paul Tarjan

5
你可以让vim为你加密。在vim中保存文件之前,通过gpg加密器过滤内容:
:{range}![!]{filter} [!][arg]命令可以将{range}范围内的行通过外部程序{filter}进行过滤。Vim会用最新的命令替换可选的感叹号,并附加可选的[arg]。Vim将过滤命令的输出保存在临时文件中,然后将文件读入缓冲区。Vim使用'shellredir'选项将过滤器输出重定向到临时文件。但是,如果'shelltemp'选项关闭,则在可能的情况下(在Unix上),将使用管道。当'cpoptions'中包含'R'标志时,除非使用|:keepmarks|命令,否则删除过滤行中的标记。例如:> :keepmarks '<,'>!sort< 当过滤后的行数少于之前时,缺失行中的标记仍将被删除。所以,如果你想通过gpg过滤(我猜测这里的标志):
:%!gpg -q -d -p $password
:w $filename

如果您想在vim中使用它们,您需要导出$password$filename环境变量,以便vim可以访问它们。


谢谢。看起来这个方法可能可行。我正在尝试简化我的密码管理流程。目前我执行以下操作:gpg passwords.gpg vim passwords gpg -c passwords rm passwords这需要输入3个密码并将文件存储在磁盘上。我希望避免这种情况。您提到的“vim命令”方法是否更好? - Paul Tarjan
1
当然,你可以使用 vim passwords.gpg 命令打开文件,然后在 vim 中输入 :%!gpg - 命令来解密文件,进行编辑,再使用 :%!gpg -e 命令重新加密文件,最后使用 :w 命令保存。你甚至可以设置 autocmds,在打开 .gpg 文件时自动解密并在写入时重新加密(这样可以避免意外将未解密的版本写入磁盘)。 - rampion
抱歉,应该是-d来解密。 - rampion
这应该被翻译为“写入时重新加密”。 - rampion
我认为自动命令应该是 au BufReadPre *.gpg %!gpg -dau BufWritePre *.gpg %!gpg -e,但我还没有测试过。实际上,定义BufReadCmd和BufWriteCmd可能更好(但需要更多的工作)。 - rampion
非常详细的回答,谢谢。我正在使用口令对称加密,这样我就可以在任何地方使用这个密码文件,而不需要在该位置使用gpg私钥。使用您的第一种方法,我仍然需要输入密码3次,遗憾的是这对用户来说并不是无缝的。自动命令看起来很有趣,它们可以放在vim命令行的某个地方吗? - Paul Tarjan

3
你可以挂载一个tmpfs文件系统(将数据存储在内存中)并在那里完成你的工作。
编辑:对不起,应该是ramfs。区别在于tmpfs可以分页到交换空间,这是不想要的。或者,你可以关闭所有交换设备(使用swapoff命令),并使用tmpfs。

有趣。这个在用户空间可用吗?mount -t ramfs ramfs /tmp/ramfs/ mount: 只有 root 用户可以执行此操作。如果用户可以执行,那将更优,因为我希望这种方法可以在我没有 root 权限的机器上工作。 - Paul Tarjan

2

另一个选择是使用Vim的gnupg插件,而不是重复造轮子。 :) 当然,我有一点偏见,因为我是该插件的当前维护者。


1

你可以使用%p命令将正在编辑的文件打印到标准输出。

在这个例子中,Vim读取它的标准输入并将其发送到标准输出:

$ (echo one; echo two; echo three; echo four) | \
  vim - -nes -u NONE  -c ':%p' -c ':q!' | \
  tail -n +2 | \
  grep t
two
three

注释:

  • vim -:从标准输入读取
  • -n:不创建交换文件,仅使用内存
  • -e:以 ex 模式启动
  • -s:静默或批处理模式
  • -u NONE:不读取 .vimrc(如果您希望读取您的 .vimrc,请跳过此选项。但是不同的人有不同的 .vimrc,因此如果您不写 -u NONE,您的代码可能无法为其他人甚至是您自己(如果更改了您的 .vimrc)工作。)
  • -c <something>:运行某些命令
  • -c ':%p':将缓冲区的内容打印到标准输出
  • -c 'q!':退出而不保存
  • tail -n +2:丢弃 Vim 输出的第一行(即“Vim:从 stdin 读取...”行)
在下面的例子中,Vim实际上做了一些有用的事情:它删除了标准输入的第一列。
$ (echo one; echo two; echo three; echo four) | \
  vim - -nes -u NONE  -c ':exec "normal G\<c-v>ggx"' -c ':%p' -c ':q!' | \
  tail -n +2
ne
wo
hree
our

注释:

  • :exec:执行以下命令(命令行命令,不是正常模式命令)
  • normal:执行给定的正常模式命令的命令行命令
  • G:跳转到文件的最后一行
  • \<c-v>:开始块选择
  • gg:跳转到第一行
  • x:删除所选字符

编辑:

我可以告诉vim在交互会话中从“:wq”之后立即发出“:%p”吗?

您可以使用VimLeave自动命令(请参见:help autocommand:help VimLeave)告诉Vim像“在退出之前运行命令X”这样的事情:

$ (echo "line 1"; echo "line 2") | \
  vim - -nes -u NONE -c ':autocmd VimLeave * :%p'
Vim: Reading from stdin...
:q!        # you type :q!
line 1
line 2

问题出在“:%p”命令上。如果你使用“-e”参数,你将无法获得可视化编辑器。如果你不使用“-e”,Vim不会将“:%p”的结果打印到标准输出,而是打印到终端。
我会在另一篇文章中提出不同的建议。

谢谢!这已经非常接近了。我之前没有提到我也想要一个交互式的vim会话。编写一个编辑我的密码文件的脚本非常困难,而且我也不希望我的编辑出现在进程列表中。我能否告诉vim在交互式会话中从":wq"后立即执行“:%p”命令? - Paul Tarjan

1
一个解决原问题的方案(就我所看到的): < p > secret.txt 包含加密文本。

< p > ./encode.sh 是对称编码器脚本。

< p > 以下命令将从 secret.txt 中读取文本,解码,在 Vim 中进行编辑,编码,并将其写回 secret.txt:

$ cp secret.txt | \
  ./encode.sh | \
  vim - -n -u NONE \
      -c ':autocmd VimLeave * :exec "%!./encode.sh" | write! tmp'; \
  mv tmp secret.txt

注:

  • :autocmd VimLeave * <command>:在离开Vim时对任何文件执行<command>
  • :exec "%!./encode.sh" | write! secret.txt:将./encode.sh运行为整个缓冲区内容的过滤器,然后将缓冲区写入secret.txt
  • “作为过滤器”意味着缓冲区的内容将被输入到./encode.sh中。在./encode.sh完成执行后,缓冲区的内容将被替换为其标准输出。

但我必须说,这个解决方案很丑陋。我建议与rampion相同:查看提到的自动命令(:help autocommand)并尝试在Vim内完成整个编辑过程。

最后一点说明:Vim有自己的加密命令,它完全可以做到你在项目网页上所描述的:“它只是将文件解密为只有您能读取的文件,您可以在您喜欢的编辑器中进行编辑,然后重新加密并将文件保存回原来的位置。”

Vim帮助文档(:help :X)对算法的描述如下:

  • 使用的算法是可破解的。一个4个字符的密钥需要大约1小时,一个6个字符的密钥需要一天(在Pentium 133 PC上)。这要求您知道文件中必须出现的一些文本。专家可以为任何密钥破解它。当文本被解密时,这也意味着密钥可以被揭示,并且使用相同密钥加密的其他文件可以被解密。
  • Pkzip使用相同的加密方式,美国政府没有反对其出口。Pkzip的公共文件APPNOTE.TXT详细描述了此算法。

请注意,Vim现在拥有新的更安全的算法,默认为blowfish2。这里引用的注释仅适用于“set cryptmethod=zip”。 - DimeCadmium

1

没错:Vim不会输出到管道。Vim不会将其输入输出到管道中,而是将其输出到编辑器窗口中,您可以在其中进行编辑。您无法在vim之后继续管道序列。因此,请尝试:

echo $passw | gpg -q -d --passphrase-fd 0 $filename | vim -

或者根本不使用vim。

如果您想在将文件传回gpg之前编辑该文件,则必须进行两个步骤的处理。


我目前正在进行这项操作,但是文件存储在磁盘上。如果我的计算机重新启动,并且有人取走了硬盘,他们可以获取到我所有的密码。虽然不太可能,但仍然不理想。执行此操作的脚本为 http://github.com/ptarjan/gpg-encrypt/tree/master - Paul Tarjan
你可以在操作之后对文件进行“shred”操作,以0的形式重写它。 - Dan
好的,添加了 shred 选项。现在唯一的竞争条件是在我保存它之后但在我删除它之前。竞争条件很糟糕,但这个循环中没有用户输入。 - Paul Tarjan
从shred手册页面中可以看到:“注意:请注意,shred依赖于一个非常重要的假设:文件系统会在原地覆盖数据。” 我的大多数文件系统都是日志型的。那么是否有必要使用shred呢? - Paul Tarjan
好的,注意到了...我忘记了那个。看起来我的“简单粗暴”的方法可能行不通,你可能需要使用其他一些更为复杂的方法,这些方法是其他人回答中提供的。我认为shred在大多数日志文件系统上仍然有效,但在某些特殊情况下不能保证有效。特别是当磁盘开始变满时...但我对此并不完全确定。 - Dan
不过这个对你没有任何帮助,但如果你和我一样好奇的话,请看我刚刚发布的问题: https://dev59.com/WnNA5IYBdhLWcg3wjOve - Dan

0

即使在内存中,您也无法向根用户隐藏任何内容(他们可以访问/dev/mem)。这是一个不可避免的事实。

因此,我建议您只使用位于主目录下的临时文件,这些文件应该受到除根用户以外的所有人的保护。在一行中输入:

echo "testing" >~/proc$$ ; vim ~/proc$$ ;
    grep "good" ~/proc$$ ; rm -f ~/proc$$

如果您想要真正的安全性,将文件移动到一个只有您是根用户的盒子上,并在那里进行操作。然后再将加密文件移动回来。


使用 mktemp 并从该文件中进行工作一样好吗?这是我的脚本:http://github.com/ptarjan/gpg-encrypt/tree/master - Paul Tarjan
可能可以(我从未使用过mktemp),但该文件不仅可供您阅读。root用户可以做任何事情。唯一的解决方案是除了在您完全信任的盒子上(包括“内存中”)之外,不要在任何地方放置未加密的文件。 - paxdiablo
是的,可悲的是我只是在增加更多障碍来窃取文件,并永远不会完全安全。不过观点很好。 - Paul Tarjan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接