How do I execute any command editing its file (

2019-01-12 21:16发布

I have a file temp.txt, that I want to sort with the sort command in bash.

I want the sorted results to replace the original file.

This doesn't work for example (I get an empty file):

sortx temp.txt > temp.txt

Can this be done in one line without resorting to copying to temporary files?


EDIT: The -o option is very cool for sort. I used sort in my question as an example. I run into the same problem with other commands:

uniq temp.txt > temp.txt.

Is there a better general solution?

14条回答
淡お忘
2楼-- · 2019-01-12 21:58

Here you go, one line:

sort temp.txt > temp.txt.sort && mv temp.txt.sort temp.txt

Technically there's no copying to a temporary file, and the 'mv' command should be instant.

查看更多
手持菜刀,她持情操
3楼-- · 2019-01-12 21:58

This would be highly memory constrained, but you could use awk to store the intermediate data in memory, and then write it back out.

uniq temp.txt | awk '{line[i++] = $0}END{for(j=0;j<i;j++){print line[j]}}' > temp.txt
查看更多
地球回转人心会变
4楼-- · 2019-01-12 21:59

Here's a more general approach, works with uniq, sort and whatnot.

{ rm file && uniq > file; } < file
查看更多
虎瘦雄心在
5楼-- · 2019-01-12 22:00

An alternative to sponge with the more common sed:

sed -ni r<(command file) file

It works for any command (sort, uniq, tac, ...) and uses the very well known sed's -i option (edit files in-place).

Warning: Try command file first because editing files in-place is not safe by nature.


Explanation

Firstly, you're telling sed not to print the (original) lines (-n option), and with the help of the sed's r command and bash's Process Substitution, the generated content by <(command file) will be the output saved in place.


Making things even easier

You can wrap this solution into a function:

ip_cmd() { # in place command
    CMD=${1:?You must specify a command}
    FILE=${2:?You must specify a file}
    sed -ni r<("$CMD" "$FILE") "$FILE"
}

Example

$ cat file
d
b
c
b
a

$ ip_cmd sort file
$ cat file
a
b
b
c
d

$ ip_cmd uniq file
$ cat file
a
b
c
d

$ ip_cmd tac file
$ cat file
d
c
b
a

$ ip_cmd
bash: 1: You must specify a command
$ ip_cmd uniq
bash: 2: You must specify a file
查看更多
beautiful°
6楼-- · 2019-01-12 22:01

To add the uniq capability, what are the downsides to:

sort inputfile | uniq | sort -o inputfile
查看更多
Juvenile、少年°
7楼-- · 2019-01-12 22:05

A sort needs to see all input before it can start to output. For this reason, the sort program can easily offer an option to modify a file in-place:

sort temp.txt -o temp.txt

Specifically, the documentation of GNU sort says:

Normally, sort reads all input before opening output-file, so you can safely sort a file in place by using commands like sort -o F F and cat F | sort -o F. However, sort with --merge (-m) can open the output file before reading all input, so a command like cat F | sort -m -o F - G is not safe as sort might start writing F before cat is done reading it.

While the documentation of BSD sort says:

If [the] output-file is one of the input files, sort copies it to a temporary file before sorting and writing the output to [the] output-file.

Commands such as uniq can start writing output before they finish reading the input. These commands typically do not support in-place editing (and it would be harder for them to support this feature).

You typically work around this with a temporary file, or if you absolutely want to avoid having an intermediate file, you could use a buffer to store the complete result before writing it out. For example, with perl:

uniq temp.txt | perl -e 'undef $/; $_ = <>; open(OUT,">temp.txt"); print OUT;'

Here, the perl part reads the complete output from uniq in variable $_ and then overwrites the original file with this data. You could do the same in the scripting language of your choice, perhaps even in Bash. But note that it will need enough memory to store the entire file, this is not advisable when working with large files.

查看更多
登录 后发表回答