bash 如何在命令中使用文件并将输出重定向到同一个文件而不截断它?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6696842/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I use a file in a command and redirect output to the same file without truncating it?
提问by mike
Basically I want to take as input text from a file, remove a line from that file, and send the output back to the same file. Something along these lines if that makes it any clearer.
基本上我想从文件中获取输入文本,从该文件中删除一行,然后将输出发送回同一个文件。如果这让它更清楚的话,沿着这些路线的东西。
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > file_name
however, when I do this I end up with a blank file. Any thoughts?
但是,当我这样做时,我最终会得到一个空白文件。有什么想法吗?
采纳答案by c00kiemon5ter
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.
你不能这样做,因为 bash 首先处理重定向,然后执行命令。所以当 grep 查看 file_name 时,它已经是空的。不过,您可以使用临时文件。
#!/bin/sh
tmpfile=$(mktemp)
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > ${tmpfile}
cat ${tmpfile} > file_name
rm -f ${tmpfile}
like that, consider using mktemp
to create the tmpfilebut note that it's not POSIX.
像那样,考虑使用mktemp
来创建tmpfile但请注意它不是 POSIX。
回答by Lynch
回答by Manny D
Use sed instead:
使用 sed 代替:
sed -i '/seg[0-9]\{1,\}\.[0-9]\{1\}/d' file_name
回答by sailesh ramanam
try this simple one
试试这个简单的
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name | tee file_name
Your file will not be blank this time :) and your output is also printed to your terminal.
这次您的文件不会是空白的 :) 并且您的输出也会打印到您的终端。
回答by kenorb
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 as tee
, sponge
, sed -i
or any other tool which can write results to the file (e.g. sort file -o file
).
您不能对同一个文件使用重定向运算符 (>
或>>
),因为它具有更高的优先级,并且会在调用命令之前创建/截断文件。为了避免这种情况,你应该使用合适的工具,例如tee
,sponge
,sed -i
或任何其他工具,它可以将结果写到文件(例如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):
基本上将输入重定向到相同的原始文件没有意义,您应该为此使用适当的就地编辑器,例如 Ex 编辑器(Vim 的一部分):
ex '+g/seg[0-9]\{1,\}\.[0-9]\{1\}/d' -scwq file_name
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
commands
'+cmd'
/-c
- 运行任何 Ex/Vim 命令g/pattern/d
- 使用global(help :g
)删除匹配模式的行-s
- 静音模式 (man ex
)-c wq
- 执行:write
和:quit
命令
You 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?
您可能会使用它sed
来实现相同的(如其他答案中所示),但是就地( -i
) 是非标准的 FreeBSD 扩展(在 Unix/Linux 之间的工作方式可能不同)并且基本上它是流编辑器,而不是文件编辑器. 参见:Ex 模式有什么实际用途吗?
回答by w00t
One liner alternative - set the content of the file as variable:
一种班轮替代方案 - 将文件的内容设置为变量:
VAR=`cat file_name`; echo "$VAR"|grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' > file_name
回答by Zack Morris
Since this question is the top result in search engines, here's a one-liner based on https://serverfault.com/a/547331that uses a subshell instead of sponge
(which often isn't part of a vanilla install like OS X):
由于这个问题是搜索引擎中的最高结果,这里有一个基于https://serverfault.com/a/547331的单行,它使用子外壳而不是sponge
(通常不是像 OS X 这样的香草安装的一部分) :
echo "$(grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name)" > file_name
The general case is:
一般情况是:
echo "$(cat file_name)" > file_name
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.- Command substitution strips trailing newlines (this is a bug/feature of shells like bash) so we should append a postfix character like
x
to the output and remove it on the outside via parameter expansion of a temporary variablelike${v%x}
. - Using a temporary variable
$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. - Another bug/feature of shells like bash is that command substitution strips unprintable characters like
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 notbe used on binary files or anything using unprintable characters, as Lynch pointed out.
printf '%s' <string>
应该使用而不是echo <string>
这样,包含的文件-n
不会导致不良行为。- 命令替换去除尾随的换行符(这是 bash 之类的 shell 的错误/功能),因此我们应该
x
在输出中附加一个后缀字符,并通过临时变量的参数扩展(如${v%x}
. - 使用临时变量会
$v
破坏$v
当前 shell 环境中任何现有变量的值,因此我们应该将整个表达式嵌套在括号中以保留先前的值。 - 像 bash 这样的 shell 的另一个错误/功能是命令替换会
null
从输出中删除不可打印的字符。我通过调用dd if=/dev/zero bs=1 count=1 >> file_name
并以十六进制查看它来验证这一点cat file_name | xxd -p
。但是echo $(cat file_name) | xxd -p
被剥光了。因此,正如Lynch 指出的那样,此答案不应用于二进制文件或任何使用不可打印字符的内容。
The general solution (albiet slightly slower, more memory intensive and still stripping unprintable characters) is:
一般的解决方案(虽然稍微慢一点,更多的内存密集型,仍然剥离不可打印的字符)是:
(v=$(cat file_name; printf x); printf '%s' ${v%x} > file_name)
Test from https://askubuntu.com/a/752451:
从https://askubuntu.com/a/752451测试:
printf "hello\nworld\n" > file_uniquely_named.txt && for ((i=0; i<1000; i++)); do (v=$(cat file_uniquely_named.txt; printf x); printf '%s' ${v%x} > file_uniquely_named.txt); done; cat file_uniquely_named.txt; rm file_uniquely_named.txt
Should print:
应该打印:
hello
world
Whereas calling cat file_uniquely_named.txt > file_uniquely_named.txt
in the current shell:
而cat file_uniquely_named.txt > file_uniquely_named.txt
在当前 shell 中调用:
printf "hello\nworld\n" > file_uniquely_named.txt && for ((i=0; i<1000; i++)); do cat file_uniquely_named.txt > file_uniquely_named.txt; done; cat file_uniquely_named.txt; rm file_uniquely_named.txt
Prints an empty string.
打印一个空字符串。
I haven't tested this on large files (probably over 2 or 4 GB).
我还没有在大文件(可能超过 2 或 4 GB)上测试过这个。
I have borrowed this answer from Hart Simhaand kos.
我从Hart Simha和kos借用了这个答案。
回答by laktak
You can do that using process-substitution.
您可以使用process-substitution来做到这一点。
It's a bit of a hack though as bash opens all pipes asynchronously and we have to work around that using sleep
so YMMV.
尽管 bash 异步打开所有管道,但这有点黑客,我们必须使用sleep
so YMMV解决这个问题。
In your example:
在你的例子中:
grep -v 'seg[0-9]\{1,\}\.[0-9]\{1\}' file_name > >(sleep 1 && cat > file_name)
>(sleep 1 && cat > file_name)
creates a temporary file that receives the output from grepsleep 1
delays for a second to give grep time to parse the input file- finally
cat > file_name
writes the output
>(sleep 1 && cat > file_name)
创建一个接收 grep 输出的临时文件sleep 1
延迟一秒让 grep 有时间解析输入文件- 最后
cat > file_name
写入输出
回答by Steven Penny
回答by nerx
There's also ed
(as an alternative to sed -i
):
还有ed
(作为替代sed -i
):