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!

2

4 Answers 4

23

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).

4
  • 2
    If one is using bash 4.3 or later, printf '%(%c)T\n' -1 >>test.txt is even more efficient than date >>test.txt. Commented Sep 1, 2020 at 13:42
  • @CharlesDuffy Very good. I just added printf '%(%c)T\n' -1 >>test.txt to the answer.
    – John1024
    Commented Sep 1, 2020 at 18:44
  • mv does not overwrite; you need to use ...>file.new && rm file && mv file.new file or ...>file.new && mv -f file.new file.
    – Naghi
    Commented Feb 27 at 16:37
  • @Naghi By default, It is common for users to have shell aliases set in ~/.bashrc or elsewhere that override default behaviors. In the case of mv, such an alias may apply the -i option to make overwrites interactive or the -n option to prevent them altogether. Without such an override, it is the default behavior for mv simply and quietly overwrite an existing target file.
    – John1024
    Commented Mar 22 at 5:30
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.

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

2
  • 1
    This should be <<<"$(<file)" and, later, <<<"$(date)"$'\n'"$(<file)" to avoid a bug in versions of bash older than 4.3 where herestring contents were, when unquoted, subject to string-splitting (followed by being re-joined on the first character in IFS). Commented Sep 1, 2020 at 20:12
  • 1
    I would also strongly suggest replacing echo -e "$(date)\n$(<file)" with printf '%s\n' "$(date)" "$(<file)" for the reasons described in Why is printf better than echo? -- to summarize, even if the shell is certain to be bash, how echo -e behaves depends on flags that can be modified at compile-time or runtime; when bash's echo is behaving in a standards-compliant way, echo -e foo prints -e foo on output, instead of treating -e as an option to consume. Commented Sep 1, 2020 at 20: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.

1
  • 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. Commented Sep 1, 2020 at 20:16

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .