8

The shell standard output redirects to the last line of the file, is there a way to write it to the first line of the file?

Since the content of stdout is unpredictable, I suspect that this may require changing the default behavior of stdout, but I am not sure if it is feasible.

Example, redirecting a timestamp to file

echo `date` >> test.txt

Default save to last line of file

Mon Aug 31 00:40:27 UTC 2020
Mon Aug 31 00:40:28 UTC 2020
Mon Aug 31 00:40:29 UTC 2020
Mon Aug 31 00:40:30 UTC 2020

Desired effect, save the output to the first line of the file

Mon Aug 31 00:40:30 UTC 2020
Mon Aug 31 00:40:29 UTC 2020
Mon Aug 31 00:40:28 UTC 2020
Mon Aug 31 00:40:27 UTC 2020

Thanks in advance!

Matthew
  • 351
  • 2
  • 14

4 Answers4

22

To write the date to the beginning instead of the end of file, try:

{ date; cat file; } >file.new && mv file.new file

Discussion

  1. Adding new text to the beginning of a file requires the whole file to be rewritten. Adding new text to the end of a file only requires writing the new text.

  2. Andy Dalton's suggestion of just appending to the end of a file like normal and then using tac file to view the file is a good one.

  3. echo `date` >> test.txt can be replaced by a simpler and more efficient date >> test.txt.

    If one is using bash 4.3 or later then, as Charles Duffy points out, printf '%(%c)T\n' -1 >>test.txt is still more efficient.

  4. The spaces around the curly braces are essential. This is because { and } are shell reserved words (as opposed to shell keywords which do not require spaces).

John1024
  • 73,527
  • 11
  • 167
  • 163
12

Try using sed :

sed -i "1 i\
$(date)" test.txt

Form man :

i \
text Insert text, which has each embedded newline preceded by a backslash.

Siva
  • 9,017
  • 8
  • 56
  • 86
3

Reverse order of lines

ex -s +%g/^/m0 +wq file

ex -s mode of vim editor, equivalent vim -nes
+wq command save and exit
or for bash

tac <<<$(<file) >file

<<<$(<file) this design serves as a self-made buffer

Writing to the first line

cat <<<$(date)$'\n'$(<file) >file
echo -e "$(date)\n$(<file)" >file

and

ex -s +'0r!date' +wq file

Sorry, I didn't get it right at first

nezabudka
  • 2,376
  • 5
  • 15
1

The command

echo `date` > test.txt

truncates the file test.txt and writes into it

While the command

echo `date` >> test.txt

writes to the file appending to the end

There is not a way in the shell to write without writing at the end (either by first truncating or by appending).

Lines are just a bunch of files with a delimiter. Prepending a line requires rewriting the whole file. However, it is possible to open a file for read-write and overwrite the existing bytes. Although the shell doesn't give you a direct interface for that, you may use for instance dd to get such effect:

date | dd of=test.txt conv=notrunc

This would replace the first date with the current one. Also note that we are taking advantage that it is outputting in a format with a fixed width,* so we are just replacing the first record. Otherwise, if each line had different length, we would probably end up with a mess of partial ends of old lines written at the beginning of the file.

(*) Beware that if the timezone varies, so does the length. Specially when changing from/to DST. date -R would be a more stable format.

Ángel
  • 3,383
  • 1
  • 13
  • 16
  • Since NULs aren't rendered by an ANSI-compliant terminal, one could have a file start with a fixed length of nothing but NULs, and later replace that block with an updated header and the same number of NULs; it would look like the changing first line, and would be able to modify its length so long as it never overruns the NUL-padded region. – Charles Duffy Sep 01 '20 at 20:16