如何在命令中使用文件并将输出重定向到同一文件而不截断它?
基本上我想从文件中获取输入文本,从该文件中删除一行,然后将输出发送回同一文件。如果这样可以让事情变得更清楚的话。
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > file_name
然而,当我这样做时,我最终得到一个空白文件。 有什么想法吗?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(14)
使用 sponge 来完成此类任务。它是 moreutils 的一部分。
尝试这个命令:
Use sponge for this kind of tasks. Its part of moreutils.
Try this command:
您不能这样做,因为 bash 首先处理重定向,然后执行命令。因此,当 grep 查看 file_name 时,它已经是空的。不过,您可以使用临时文件。
像这样,考虑使用
mktemp
创建 tmpfile 但请注意,它不是 POSIX。You cannot do that because bash processes the redirections first, then executes the command. So by the time grep looks at file_name, it is already empty. You can use a temporary file though.
like that, consider using
mktemp
to create the tmpfile but note that it's not POSIX.使用 sed 代替:
Use sed instead:
尝试这个简单的方法,
这次您的文件将不会是空白的:)并且您的输出也会打印到您的终端。
try this simple one
Your file will not be blank this time :) and your output is also printed to your terminal.
这是很有可能的,您只需确保在写入输出时,将其写入不同的文件即可。这可以通过在打开文件描述符之后但在写入文件之前删除该文件来完成:
或者逐行,以便更好地理解它:
这仍然是一件有风险的事情,因为如果 COMMAND 无法正常运行,您将将丢失文件内容。如果 COMMAND 返回非零退出代码,可以通过恢复文件来缓解这种情况:
我们还可以定义一个 shell 函数以使其更易于使用:
示例:
另外,请注意,这将保留原始文件的完整副本(直到第三个文件描述符已关闭)。如果您使用的是 Linux,并且您正在处理的文件太大,无法在磁盘上容纳两次,您可以查看 此脚本,它将通过管道将文件逐块传输到指定的命令,同时取消分配已处理的块。与往常一样,请阅读使用页面中的警告。
This is very much possible, you just have to make sure that by the time you write the output, you're writing it to a different file. This can be done by removing the file after opening a file descriptor to it, but before writing to it:
Or line by line, to understand it better :
It's still a risky thing to do, because if COMMAND fails to run properly, you'll lose the file contents. That can be mitigated by restoring the file if COMMAND returns a non-zero exit code :
We can also define a shell function to make it easier to use :
Example :
Also, note that this will keep a full copy of the original file (until the third file descriptor is closed). If you're using Linux, and the file you're processing on is too big to fit twice on the disk, you can check out this script that will pipe the file to the specified command block-by-block while unallocating the already processed blocks. As always, read the warnings in the usage page.
您不能对同一文件使用重定向运算符(
>
或>>
),因为它具有更高的优先级,并且会在之前创建/截断文件该命令甚至被调用。为了避免这种情况,您应该使用适当的工具,例如tee
、sponge
、sed -i
或任何其他可以将结果写入文件的工具(例如排序文件-o文件
)。基本上将输入重定向到同一个原始文件是没有意义的,您应该为此使用适当的就地编辑器,例如 Ex 编辑器(Vim 的一部分):
其中:
'+cmd'
/< code>-c - 运行任何 Ex/Vim 命令g/pattern/d
- 使用 全局 (help :g
)-s
- 静默模式 (man ex
)- c wq
- 执行:write
和:quit
命令您可以使用
sed
来实现相同的目的(如其他答案中所示) ),但是就地 (-i
) 是非标准 FreeBSD 扩展(在 Unix/Linux 之间工作方式可能不同),基本上它是一个 stream editor,而不是文件编辑器。请参阅:Ex 模式有任何实际用途吗?You can't use redirection operator (
>
or>>
) to the same file, because it has a higher precedence and it will create/truncate the file before the command is even invoked. To avoid that, you should use appropriate tools such astee
,sponge
,sed -i
or any other tool which can write results to the file (e.g.sort file -o file
).Basically redirecting input to the same original file doesn't make sense and you should use appropriate in-place editors for that, for example Ex editor (part of Vim):
where:
'+cmd'
/-c
- run any Ex/Vim commandg/pattern/d
- remove lines matching a pattern using global (help :g
)-s
- silent mode (man ex
)-c wq
- execute:write
and:quit
commandsYou may use
sed
to achieve the same (as already shown in other answers), however in-place (-i
) is non-standard FreeBSD extension (may work differently between Unix/Linux) and basically it's a stream editor, not a file editor. See: Does Ex mode have any practical use?由于这个问题是搜索引擎中的最高结果,因此这里有一个基于 https://serverfault.com/a/547331 使用 subshell 而不是
sponge
(它通常不是 OS X 等普通安装的一部分):一般情况是:
编辑,上述解决方案有一些注意事项:
printf '%s'
而不是echo
,这样包含-n
的文件就不会导致不良行为。x
,并通过 临时变量的参数扩展,如${v%x}
。$v
会破坏当前 shell 环境中任何现有变量$v
的值,因此我们应该将整个表达式嵌套在括号中以保留以前的值。null
。我通过调用 dd if=/dev/zero bs=1 count=1 >> 验证了这一点file_name 并使用cat file_name | 以十六进制查看它xxd -p
。但是 echo $(cat file_name) | xxd -p 被删除。因此,这个答案不应该用于二进制文件或任何使用不可打印字符的内容,如 林奇指出。一般解决方案(虽然稍慢,内存消耗更大,并且仍然剥离不可打印的字符)是:
从 https:// 进行测试askubuntu.com/a/752451:
应该打印:
而调用
cat file_uniquely_named.txt >当前 shell 中的 file_uniquely_named.txt
:打印空字符串。
我还没有在大文件(可能超过 2 或 4 GB)上进行过测试。
我从 Hart Simha 和 科斯。
Since this question is the top result in search engines, here's a one-liner based on https://serverfault.com/a/547331 that uses a subshell instead of
sponge
(which often isn't part of a vanilla install like OS X):The general case is:
Edit, the above solution has some caveats:
printf '%s' <string>
should be used instead ofecho <string>
so that files containing-n
don't cause undesired behavior.x
to the output and remove it on the outside via parameter expansion of a temporary variable like${v%x}
.$v
stomps the value of any existing variable$v
in the current shell environment, so we should nest the entire expression in parentheses to preserve the previous value.null
from the output. I verified this by callingdd if=/dev/zero bs=1 count=1 >> file_name
and viewing it in hex withcat file_name | xxd -p
. Butecho $(cat file_name) | xxd -p
is stripped. So this answer should not be used on binary files or anything using unprintable characters, as Lynch pointed out.The general solution (albiet slightly slower, more memory intensive and still stripping unprintable characters) is:
Test from https://askubuntu.com/a/752451:
Should print:
Whereas calling
cat file_uniquely_named.txt > file_uniquely_named.txt
in the current shell:Prints an empty string.
I haven't tested this on large files (probably over 2 or 4 GB).
I have borrowed this answer from Hart Simha and kos.
一种替代方案 - 将文件的内容设置为变量:
One liner alternative - set the content of the file as variable:
下面的代码将完成与
sponge
相同的事情,而不需要moreutils
:--random-source=/dev/zero
部分技巧 < code>shuf 执行它的操作而不进行任何洗牌,因此它将缓冲您的输入而不改变它。然而,出于性能原因,使用临时文件确实是最好的。因此,这是我编写的一个函数,它将以通用的方式为您完成此操作:
The following will accomplish the same thing that
sponge
does, without requiringmoreutils
:The
--random-source=/dev/zero
part tricksshuf
into doing its thing without doing any shuffling at all, so it will buffer your input without altering it.However, it is true that using a temporary file is best, for performance reasons. So, here is a function that I have written that will do that for you in a generalized way:
在我遇到的大多数情况下,这都很好地解决了这个问题:
请注意,虽然
$(…)
去除尾随换行符,<<<
确保< /em> 最后的换行符,所以通常结果是令人惊奇的令人满意。(如果您想了解更多信息,请在
man bash
中查找“Here Strings”。)完整示例:
这不会截断文件并产生:
请注意,为了清楚起见,我在这里使用了一个函数可扩展性,但这不是必需的。
一个常见的用例是 JSON 版本:
这会产生:
This does the trick pretty nicely in most of the cases I faced:
Note that while
$(…)
strips trailing newlines,<<<
ensures a final newline, so generally the result is magically satisfying.(Look for “Here Strings” in
man bash
if you want to learn more.)Full example:
This does not truncate the file and yields:
Note that I used a function here for the sake of clarity and extensibility, but that’s not a requirement.
A common usecase is JSON edition:
This yields:
还有
ed
(作为sed -i
的替代品):There's also
ed
(as an alternative tosed -i
):您可以将 slurp 与 POSIX Awk 结合使用:
示例
You can use slurp with POSIX Awk:
Example
我通常使用 tee 程序来执行此操作:
它自行创建和删除临时文件。
I usually use the tee program to do this:
It creates and removes a tempfile by itself.
试试这个
Try this