17

I can do cat file.txt to get the contents of a file but I also want to tack on a final line of my own choosing.

I tried piping (cat file.txt ; echo "My final line") | but only the final line is getting passed through the pipe. How can I join the cat and final line?

Edit for a point of clarification: I do not wish to modify the file itself. I am aware that if this were the case I could do echo "My final line" >> file.txt or echo "My final line" | tee -a file.txt but I am only trying to do the append within the context of this particular command so I can pipe in the concatenation of file.txt and "My final line".

DoubleBass
  • 337
  • 1
  • 3
  • 9
  • 1
    Maybe something like `echo "My final line" >> file.txt`? – edwinksl Oct 24 '17 at 14:06
  • I don't want to actually append the line to the file object, just in the command / piping. I already know that I can append the line with `>>` if I wanted to edit the file itself. – DoubleBass Oct 24 '17 at 14:09
  • 11
    Have you accidentally erased `file.txt`? Your pipe snippet should work. – Bracken Oct 24 '17 at 14:15
  • @DoubleBass: Mathieu Mitchell's answer is a better one, as it doesn't modify the file (either in-memory or on disk), but instead utilizes a feature of a built-in command. – Bryan Boettcher Oct 24 '17 at 17:26

5 Answers5

39

You can leverage cat's ability to read from stdin combined with it's ability to read multiple files to achieve this.

~$ cat file.txt
Hello from file.txt

~$ echo "My final line" | cat file.txt -
Hello from file.txt
My final line

You can also prepend a line, as such:

~$ echo "My final line" | cat - file.txt
My final line
Hello from file.txt

Note that you are not limited to a single line. cat will read from stdin until it reaches EOF. You can pass the output from curl, for example, to prepend or append to the output of cat.

~$ curl -s http://perdu.com | cat file.txt -
Hello from file.txt
<html><head><title>Vous Etes Perdu ?</title></head><body><h1>Perdu sur l'Internet ?</h1><h2>Pas de panique, on va vous aider</h2><strong><pre>    * <----- vous &ecirc;tes ici</pre></strong></body></html>
  • 1
    `(cat file.txt ; echo "My final line") | …` would also do it. But using `cat` for what it was designed for is better +1. – ctrl-alt-delor Oct 24 '17 at 21:30
  • @ctrl-alt-delor: Sub-shells also have the disadvantage of an additional process to fork which is relatively expensive. Try for example `echo "$$" "$BASHPID" | head -q -n 1 /proc/mounts - | cat` vs. `( head -q -n 1 /proc/mounts; echo "$$" "$BASHPID" ) | cat` or even `{ head -q -n 1 /proc/mounts; echo "$$" "$BASHPID"; } | cat`. – David Foerster Oct 25 '17 at 12:48
15

For appending a line to a file, you can just use shell append redirection operator, >> (the file will be open(2)-ed with O_APPEND flag):

echo 'My final line' >>file.txt

Now, if you want just to view the content of the file with a final line appended, i would use cat with two arguments:

  • First, your file obviously, let's say file.txt
  • Second argument would be the string of your choice, and to pass the string as a filename (as cat only deals with files) you can leverage process substitution, <(), which would return a file descriptor (/proc/self/fd/<fd_number>).

Putting these together:

cat file.txt <(echo 'My final line')

If you want the output to be paged, assuming less is your favorite pager:

less file.txt <(echo 'My final line')
heemayl
  • 90,425
  • 20
  • 200
  • 267
14
sed -e '$aMy final line' file.txt

From man sed the option -e

-e script, --expression=script
    add the script to the commands to be executed

$ matches the last line and a appends the string.

If you wanted to permanently append the line to the file, use -i

-i[SUFFIX], --in-place[=SUFFIX]
    edit files in place (makes backup if SUFFIX supplied)

Changing the command to

sed -i -e '$aMy final line' file.txt
Katu
  • 3,553
  • 25
  • 42
11

It was already clarified in the comments to the question, but adding it again as an answer here.

The command noted in the question,

(cat file.txt ; echo "My final line") | other command

works as is expected – all the output from the subshell formed by the parentheses is piped to the second command.

If the file doesn't end with a new line, the echoed string is appended to the last line – this is common with all the other solutions here, and can be solved by adding another (empty) echo before.

Paŭlo Ebermann
  • 779
  • 2
  • 6
  • 25
  • 1
    If the file doesn't end with a newline, I'd use `awk`: `awk 1 file.txt; echo "My final line"`. `awk 1 file` is a nice trick to compensate for missing EOL at the end of the file. – muru Oct 25 '17 at 03:29
  • If the file doesn't end with a new line, you can also just use `echo "\nMy final line"` without having to depend on `awk` – fregante May 31 '19 at 08:04
0

Lots of good answers here but in my case I just wanted to append a newline to some stdout, by tacking a command onto the end of the pipeline. This achieves that:

$CMD_OR_COMMANDS | cat - <(echo)

To fit the original example:

cat file.txt | cat - <(echo "My final line")

Which is admittedly a verbose way of accomplishing the thing, but the key is that this pattern might fit more of your needs, since it works on anything that produces stdout.

billkw
  • 101
  • 3