由于尚未诊断出应用程序错误,我有数百台磁盘已满的服务器。有一个文件充满了重复的行——不是日志文件,而是带有变量定义的用户环境文件(所以我不能直接删除该文件)。
我编写了一个简单的sed
命令来检查错误添加的行并将其删除,并在文件的本地副本上进行了测试。它按预期工作。
但是,当我在具有完整磁盘的服务器上尝试它时,我收到大约以下错误(它来自内存,而不是复制和粘贴):
sed: couldn't flush /path/to/file/sed8923ABC: No space left on deviceServerHostname
当然,我知道没有剩余空间了。这就是为什么我要尝试删除东西! (sed
我使用的命令会将 4000 多行文件减少到大约 90 行。)
我的sed
命令只是sed -i '/myregex/d' /path/to/file/filename
有没有办法在磁盘已满的情况下应用此命令?
(它必须是自动化的,因为我需要将其应用到数百台服务器作为快速修复。)
(显然需要诊断应用程序错误,但与此同时服务器无法正常工作......)
更新:我遇到的情况是通过删除其他我发现可以删除的内容来解决的,但我仍然想要答案这问题,这对未来和其他人都有帮助。
/tmp
是不行的;它位于同一文件系统上。
在释放磁盘空间之前,我做了测试,发现我可以vi
通过打开文件并运行来删除其中的行:g/myregex/d
,然后使用:wq
.似乎应该可以自动执行此操作,而无需求助于单独的文件系统来保存临时文件......(?)
答案1
该-i
选项并不会真正覆盖原始文件。它使用输出创建一个新文件,然后将其重命名为原始文件名。由于文件系统上没有空间容纳这个新文件,因此它会失败。
您需要在脚本中自行执行此操作,但在不同的文件系统上创建新文件。
另外,如果您只是删除与正则表达式匹配的行,则可以grep
使用sed
.
grep -v 'myregex' /path/to/filename > /tmp/filename && mv /tmp/filename /path/to/filename
一般来说,程序很少可能使用同一个文件作为输入和输出——一旦开始写入文件,程序从文件中读取的部分将不再看到原始内容。因此,它要么必须先将原始文件复制到某处,要么写入新文件并在完成后重命名。
如果您不想使用临时文件,可以尝试将文件内容缓存在内存中:
file=$(< /path/to/filename)
echo "$file" | grep -v 'myregex' > /path/to/filename
答案2
就是这样sed
工作的。如果与-i
(就地编辑)一起使用sed
,则会创建一个包含已处理文件的新内容的临时文件。完成后sed
,用临时文件替换当前工作文件。该实用程序不会编辑该文件到位。这正是每个编辑的行为。
这就像您在 shell 中执行以下任务:
sed 'whatever' file >tmp_file
mv tmp_file file
此时sed
,尝试使用系统调用将缓冲数据刷新到错误消息中提到的文件中fflush()
:
对于输出流,
fflush()
通过流的底层写入函数强制写入给定输出或更新流的所有用户空间缓冲数据。
对于您的问题,我看到了一个解决方案,安装一个单独的文件系统(例如tmpfs
,如果您有足够的内存或外部存储设备)并将一些文件移到那里,在那里处理它们,然后将它们移回来。
答案3
自从发布这个问题以来,我了解到这ex
是一个符合 POSIX 标准的程序。它几乎普遍符号链接到vim
,但无论哪种方式,以下(我认为)是ex
与文件系统相关的关键点(取自 POSIX 规范):
本节使用术语编辑缓冲区描述当前的工作文本。该术语没有暗示具体的实现。所有编辑更改都在编辑缓冲区上执行,并且在编辑器命令写入文件之前,对其进行的任何更改都不会影响任何文件。
“……会影响任何文件...”我相信在文件系统上放置一些东西(甚至是临时文件)会被视为“影响任何文件。”也许?*
仔细研究了POSIX 规范ex
ex
与在线找到的常见脚本使用(其中散布着vim
特定命令)相比,指出了有关其预期便携式使用的一些“陷阱” 。
+cmd
根据 POSIX,实现是可选的。- 允许多个
-c
选项也是可选的。 - 全局命令
:g
“吃掉”直到下一个非转义换行符的所有内容(因此在为正则表达式找到每个匹配项之后运行它,而不是在最后运行一次)。所以-c 'g/regex/d | x'
只能删除一实例,然后退出该文件。
所以根据我的研究,在完整文件系统上就地编辑文件以删除与特定正则表达式匹配的所有行的 POSIX 兼容方法是:
ex -sc 'g/myregex/d
x' /path/to/file/filename
如果您有足够的内存将文件加载到缓冲区中,这应该可以工作。
*如果您发现任何其他指示,请在评论中提及。
答案4
正如其他答案中所述,
sed -i
通过将文件复制到新文件来工作在同一目录下,在此过程中进行更改,然后将新文件移动到原始文件上。这就是为什么它不起作用。
ed
(原始行编辑器)的工作方式有些类似,但是,上次我检查时,它用于/tmp
临时文件。如果您的/tmp
文件系统与已满的文件系统不同,
ed
则可以为您完成这项工作。
试试这个(在交互式 shell 提示符下):
$ 编辑/路径/到/文件/文件名 磷 G/正则表达式/d w q
(这P
是一个首都P)并不是绝对必要的。开启提示;没有它,你就在黑暗中工作,有些人会觉得这令人不安。和是w
q
w仪式和q伊特。
ed
因诊断神秘而臭名昭著。如果在任何时候它显示除提示(即*
)以外的任何内容或明显确认操作成功的内容(尤其如果它包含一个?
),不要写入文件(用w
)。就放弃吧(q
)。如果它不让你出去,请尝试再说q
一遍。
如果您的/tmp
目录位于已满的文件系统上(或者它的文件系统也已满),请尝试在某处找到一些空间。 Chaos 提到安装 tmpfs 或外部存储设备(例如闪存驱动器);但是,如果您有多个文件系统,并且它们不是全部完整,您可以简单地使用其他现有的之一。 Chaos 建议将文件复制到其他文件系统,在那里编辑它们(使用sed
),然后将它们复制回来。此时,这可能是最简单的解决方案。但另一种方法是在具有一些可用空间的文件系统上创建一个可写目录,将环境变量设置TMPDIR
为指向该目录,然后运行ed
. (披露:我不确定这是否有效,但不会有什么坏处。)
一旦开始ed
工作,您可以通过执行以下操作来自动化此操作
编辑文件名<< 结束符 G/正则表达式/d w q EOF
在脚本中。或者 ,按照 don_crissti 的建议。printf '%s\n' 'g/myregex/d' w q | ed -s filename