1

I'm trying to create a shell script called sv that will prepend to a file, but the solution I'm using seems to only be good for one use, and them the temporary file is deleted. Is there a way I can make a shell script that will be go to use over and over?

Here's the questions: "Suppose we wish to maintain a list of all the dates when we logged on to our UNIX system. It would be easy to do this by adding the following to the .login file: date >> logdates Unfortunately, the latest date comes at the end of file logdates. I want it at the front; that is, the file should contain login dates from latest to earliest. Write a C shell script sv that will be used as follows: date | sv logdates (This way, the script is quite general, and I can use it for other cases when I want to add things to the front of a file.)"

Here's the script I've come up with:

"#!/bin/sh
cat - logdates  /tmp/out && mv /tmp/out logdates"

This will work once, when I try again the system tells me that /tmp/out doesn't exist.

Does anyone have any suggestions?

Thank you!

user1992348
  • 113
  • 1
  • 2
  • 11
  • 4
    This smells like homework. For a real-use case, an answer would be that what you were being asked to do was impractical, because running it (including what you have so far) rewrites the whole file, and thus has what are generally considered unacceptable performance characteristics. This is a necessary consequence of the design of POSIX (and C library) calls which allow efficient appends and in-place replacement, but don't have a mechanism for efficient updates which require moving other data to accomodate. – Charles Duffy Mar 12 '14 at 01:49
  • @CharlesDuffy: Performance is not the only concern in the world. Python, Ruby can be 100 times slower for some task then a corresponding optimized C version. It doesn't mean that programming in them is "impractical". Sometimes you want to [prepend some data to a file and you don't care about performance](http://stackoverflow.com/a/5356563/4279). – jfs Mar 12 '14 at 02:20
  • 1
    @J.F.Sebastian, the thing about syscall-based performance limitations is that you have them no matter what your language is -- C, Python, Ruby, whatever. So if you're smart (and dealing with large enough files that you have a significant constant factor), you **don't** just prepend some data to your file, but instead you modify the storage format such that you don't need to prepend. Maybe that means you can read from multiple files. Maybe it means you have an index you use to figure out where to seek to. Point is, you do what makes sense given the use case, and it often ISN'T prepending. – Charles Duffy Mar 12 '14 at 02:21
  • @J.F.Sebastian ...in this case, the easy answer is that you append to the file, rather than prepending to it, and read it from the bottom to the top. Bloody easy, highly performant (compared to rewriting), and eminently practical. And also much, **much** less prone to race conditions where you lose data when two different update attempts are taking place at the same time. – Charles Duffy Mar 12 '14 at 02:24
  • You should read bash script Ebooks ...It would help – MLSC Mar 12 '14 at 05:07

3 Answers3

2

Using sponge utility:

#!/bin/sh
cat - "$1" | sponge "$1"
jfs
  • 399,953
  • 195
  • 994
  • 1,670
1

Simply use tac ( reverse of cat ) to output a file in reverse

#!/bin/sh
tac logdates > /tmp/out && mv /tmp/out logdates
Newbrict
  • 285
  • 1
  • 13
1

Your shell script sv could contain the following, where newline is your data read from the | and $1 is the filename passed to sv:

#!/bin/sh
read newline
(echo "$newline"; cat $1) > tmp; mv tmp $1

And then you could use it like:

$ date > logdates
$ date >> logdates
$ cat logdates
Tue Mar 11 22:14:34 CDT 2014
Tue Mar 11 22:14:37 CDT 2014
$ date | ./sv logdates
$ cat logdates
Tue Mar 11 22:14:50 CDT 2014
Tue Mar 11 22:14:34 CDT 2014
Tue Mar 11 22:14:37 CDT 2014

This will only work for one-line appends though, as read is terminated by the newline (/n) character.

cmrust
  • 366
  • 2
  • 7
  • Thank @raptastics I added cat logdates to the sv file at the end so now when i run date | sv logdates, it'll show me what's in logdates. I'm a little confused about the difference between $newline and $1: are these two separate files? – user1992348 Mar 12 '14 at 05:00
  • $1 is a reserved variable. It is the 1st argument passed to your script, or in this case, the name of your logdates file. $0 is the 0th argument called in the shell, or the name of your script. $newline is also a variable. It is created and set by the read command. Try adding extra echo statements to see what each of the variables are set to, such as echo $1; echo $0; echo $newline – cmrust Mar 12 '14 at 16:57