Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

Suppose I have a sorted file, named sorted.txt, like this:

beautiful
easy
fast
functional
handy

Now, doing echo fine-grained >> sorted.txt will put "fine-grained" at the end of the file, after "handy". Is there a way to insert it in order, between "fast" and "functional", without needing to re-sort the file?

share|improve this question
add comment

4 Answers

up vote 3 down vote accepted

Although theoretically possible, there isn't a magic way to do this.

If you knew exactly what value you wanted to place it after you could use sed in-place search and replace to stick the new value in the file, but given the complexities of sorting, it basically comes down to you are going to have to sort it somewhere long the line.

echo fine-grained >> sorted.txt
sort sorted.txt > sorted.txt.new && mv sorted.txt{.new,}

Or with sponge:

{ echo fine-grained ; cat sorted.txt } | sort | sponge sorted.txt

Edit: Gilles made a good suggestion for using the -m argument for sort to potentially speed this up. From the manual:

-m, --merge
 merge already sorted files; do not sort

This would keep sort from processing all the way through the input, it only has to scan through the input files and figure out their relation to each other.

echo fine-grained | sort -m sorted.txt - | sponge sorted.txt
share|improve this answer
3  
sort has a -m option telling it that its arguments are sorted, so echo fine-grained | sort sorted.txt - | sponge sorted.txt might be a little more efficient (you'd only notice if sorted.txt was at least about as big as your RAM). –  Gilles Jul 17 '11 at 10:52
    
The combination of -m & sponge seems to be the solution closest to what I originally meant. Thank you! –  Philomath Jul 18 '11 at 6:05
add comment

You can't insert data in a file - there's nothing in the POSIX API for that. The best you can do is to seek past all the data that doesn't move, write the new line, and move all the later data down. This would be tricky and not easily done without writing your own program.

If you're happy to generate a new file, awk can do this fairly easily:

awk -v newline=fine-grained '
    !inserted && $0 > newline { print newline; inserted=1 }
    1
'

The first line of the script prints the line you want to insert only if it has not already done so and if the input line comes after the line you want to insert. It also records that the line was inserted.

The second line just prints out the input line (the expression 1 is true, and the default action is to print the input line, so we do not need to say { print }.

share|improve this answer
    
+1, elegant solution. –  Shadur Jul 17 '11 at 14:03
add comment

A direct answer to your question is NO, you'll need to run some type of sort somewhere along the line.

share|improve this answer
add comment

You'll have to put it through some type of sort, its' just a matter of when. But what I suspect you're looking for is a one-shot deal. Your method is up to you, and there are various ways that may be faster.

This gets the job done, but feel free to play around with other ways.

echo "$(echo fine-grained | cat - sorted.txt | sort)" > sorted.txt

Deconstructed, starting with the inner subshell to avoid truncating the file before reading it, concatenate the echoed input with the diskfile. Echoing it quoted preserves line breaks. Then sort it.

Or the compacted, with Giles' suggestion of sort -m (and avoiding UUOC).

echo "$(echo fine-grained | sort -m - sorted.txt)" > sorted.txt
share|improve this answer
1  
I tried this sequence using bash and all I got was the "fine-grained" line in the sorted.txt. What is happening of course is that sorted.txt is being cleared by the output redirection of the sort and thus the cat will only generate that from the echo. –  mdpc Jul 17 '11 at 5:15
1  
@bahamat: In spite of having mentioned the issue, your use of a command replacement subshell does nothing to keep the file from being clobbered. –  Caleb Jul 17 '11 at 7:13
    
@mdpc @caleb Sorry, sort needs to be inside the subshell. Corrected. If you can, please remove the -1. –  bahamat Jul 17 '11 at 14:16
    
@bahamat I removed my -1 because this works now, but no upvote because putting redirects on the end of a command chain is a good way to accidentally clobber your data, using a subshell as an anti-clobber solution isn't something I would teach. –  Caleb Jul 17 '11 at 20:07
    
Short & elegant, not bad at all. –  Philomath Jul 17 '11 at 20:52
show 1 more comment

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.