在没有临时(中间)文件的同一文件上处理

时间:2014-12-17 18:14:04

标签: linux bash awk sed

我正在使用awk对文件进行一些文本处理。例如,删除尾随空格。

awk '{gsub(/ +$/, "")} {print $0}' filename

这很好用。但是当我将输出重定向到原始文件时。它变成一个空文件。

temp$ awk '{gsub(/ +$/, "")} {print $0}' abc > abc
temp$ cat abc
temp$

所以我尝试了另一种方式。使用cat和pipe而不是awk的输入参数。

temp$ cat abc | awk '{gsub(/ +$/, "")} {print $0}' abc > abc
temp$ cat abc
temp$ 

仍然无效。有没有办法在不涉及中间文件的情况下实现相同的目标?

4 个答案:

答案 0 :(得分:2)

您可以使用sed -ised会为您处理

示例:

sed -i 's/[ \t]*$//g' file

答案 1 :(得分:1)

使用> abc的问题是shell首先处理重定向并在运行实际命令之前将文件abc初始化为0字节 。换句话说,你的awk命令是在一个空的0字节文件上运行的。

这是一个不仅可以用于此命令而且可以用于任何其他命令的技巧。

f='abc'
awk '{sub(/ +$/, "")} 1' "$f" | awk -c f="$f" -v RS=$'\g' 'END{printf $0 > f}'

$'\g'只是一个随机选择的不可能的记录分隔符,它会在任何文件中永远不存在,导致整行文件在一行中被读取。 Trick是在一个记录中读取整个文件,只在END部分写入输出。 这也适用于大尺寸文件。


早期解决方案: 您可以使用tee

awk '{gsub(/ +$/, "")} {print $0}' abc | tee abc

如果要在stdout上丢弃输出,请使用:

awk '{gsub(/ +$/, "")} {print $0}' abc | tee abc > /dev/null

答案 2 :(得分:1)

有几种可能的解决方案。但是请使用大文件进行测试,在我的机器上,小于~100Ko的文件将使用此文件:cat abc | tee abc > /dev/null但是当管道缓冲区已满并且随后被发送到下一个进程时会出现问题。当tee收到它写入文件的第一大块信息时,cat进程就无法从该文件中读取,这会导致数据损坏。

使用gawk 4.1+你可以选择inplace(-i)和sed一样。 看这篇文章: awk save modifications in place

如果你不能使用gawk 4.1,你仍然可以像其他人所建议的那样转换为sed inplace表达式。

否则,为了保持单线,您可以使用海绵(moreutils的一部分)重定向到相同的文件:

$ yes testing | head -n10000000 > /tmp/test
$ du /tmp/test
77M     /tmp/test
$ cat /tmp/test | sponge /tmp/test
$ du /tmp/test
77M     /tmp/test

如果您无法安装moreutils使用海绵,我建议使用简单的临时文件,然后移动文件:

$ tmp=$(mktemp)
$ echo $tmp
/tmp/tmp.Tl0v8HmdaA
$  awk '{gsub(/ +$/, "")} {print $0}' abc > $tmp
$ mv $tmp abc

答案 3 :(得分:1)

使用moreutils tools

中的sponge
Probably the most general purpose tool in moreutils so far is sponge(1), 
which lets you do things like this:
     

% sed "s/root/toor/" /etc/passwd | grep -v joey | sponge /etc/passwd

<强> e.g:

/tmp$ cat -E abc 
aaaaa    $
/tmp$ awk '{gsub(/ +$/, "")} {print $0}' abc | sponge abc 
/tmp$ cat -E abc 
aaaaa$