0

I am aware of the following options to append the output to a file:

test.py:

print "Error"
print "Warning"

test.txt:

Levels:
Debug

When I do:

python test.py > test.txt

It appends at the end of the file.

However, if I want to append at the beginning of a file, so that the output of my file looks like as follows:

Levels:
Error
Warning
Debug

Is there any straightforward way of doing this possibly without manually creating a temporary file (sed -i is OK for example).

I have tried several sed approach:

sed -i '1i\`python test.py`' test.txt

But none seems to be working.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
as3rdaccount
  • 3,711
  • 12
  • 42
  • 62
  • Why do you need to append to the beginning of a file? As far as I'm aware no operating system allows you to append to the beginning of a file. Perhaps it might be better to find a way so you don't have to append to the beginning of the file. – Dunes Aug 30 '14 at 13:06
  • You CAN do it without any tmp file (e.g. see http://stackoverflow.com/a/17331179/1745001) but it's not straight-forward by any means. – Ed Morton Aug 30 '14 at 14:10
  • For safety, you *want* to use a temp file. Renaming a file is atomic, effectively allowing you to replace the old with the new in one uninterruptible step: if the move fails, the original file is still intact. The linked answer tries to write the data to the original in-place, which means if a write fails for any reason in the middle, the original file is corrupted. – chepner Sep 01 '14 at 12:20

2 Answers2

1

An easy way to do this is to use a separate, temporary file:

python test.py | cat - test.txt > tmp && mv tmp test.txt

The - means that cat uses standard input.


I just realised that you actually want to put the text after the first line, not right at the beginning. To do this, you could use awk:

python test.py | awk 'NR==FNR{a[++n]=$0;next}1;/Levels:/{for(i=1;i<=n;++i)print a[i]}' - test.txt

NR is the overall record (line) number and FNR is the record number of the current input. NR==FNR means that the first block only operates on the first input (which in this case is the output of your python script). The output is added to a buffer a. The next means that awk skips the rest of the commands and goes to the next line.

1 is a shorthand which causes all of the lines in the file to be printed. When the line containing "Levels:" is found, each line of the buffer a is also printed.

You can use the same trick as above to write the output to a temporary file, then overwrite the original file.

Tom Fenech
  • 72,334
  • 12
  • 107
  • 141
  • Thanks @Tom French. With the first approach I get the error: No input files. – as3rdaccount Aug 30 '14 at 13:21
  • I don't know why you're getting that error. Anyway, the approach using `cat` will add the lines to the beginning of the file, **before** the first line. The second approach should do what you want. Have you tried it? – Tom Fenech Aug 30 '14 at 14:19
0

This should do it:

python test.py |
awk '
    NR==FNR { a[NR] = $0; next }
    { print (FNR==1 ? a[1] RS : "") $0 }
    END { for (i=2;i in a;i++) print a[i] > ARGV[1] }
' test.txt -

The above reads the first file into an array, then closes that file before opening the stdin stream coming from the pipe so from then on it can safely overwrite the first file with it's original contents from the array merged with the input from the pipe in whatever order you like.

Unlike awk '...' file > file, this is perfectly safe to do since it's awk rather than the shell deciding when to overwrite the file, it's only issue would be memory usage and speed if the original file is huge.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185