I’m trying to insert (or prepend) a file into another file
sed -i however this approach breaks hardlinks.
How do I solve this?
Some versions of
sed have an
-i option and my manual
gives the following description for it.
edit files in place (makes backup if SUFFIX supplied)
I’m not sure why they say “edit” and “in place” because it actually creates a new file with the updated changes. Unfortunately this causes a lot of confusion and as mentioned it breaks hardlinks.
$ cat myfile moo $ stat myfile File: ‘myfile’ Size: 4 Blocks: 8 IO Block: 4096 regular file Device: 801h/2049d Inode: 4358555 Links: 1 Access: (0600/-rw-------) Uid: (101642/user) Gid: ( 107/group) Access: 2017-06-09 13:11:40.594059471 +0100 Modify: 2017-06-09 13:11:31.425928132 +0100 Change: 2017-06-09 13:11:31.425928132 +0100 Birth: -
Make note of the
Inode number here which is
4358555 as we will
sed -i on the file.
$ sed -i 's/omg/lol/' myfile $ cat myfile moo
We tried to replace
lol which wasn’t present in the file
resulting in the same content we started with.
Let’s check the file again with the
$ stat myfile File: ‘myfile’ Size: 4 Blocks: 8 IO Block: 4096 regular file Device: 801h/2049d Inode: 6306549 Links: 1 Access: (0600/-rw-------) Uid: (101642/user) Gid: ( 107/group) Access: 2017-06-09 13:11:57.422300547 +0100 Modify: 2017-06-09 13:11:54.602260148 +0100 Change: 2017-06-09 13:11:54.602260148 +0100 Birth: -
As we can see the
Inode number has changed meaning we now have a different
file albeit with the same filename as before. It’s as if we first performed
rm myfile and then recreated it.
What we need to use is an “actual file editor” one of which is
First we will create some test files.
$ cat file1 file1 line1 file1 line2
$ cat file2 file2 line1 file2 line2
As mentioned at the start the goal is to “insert”
or you could also say we want to “prepend”
resulting in the following output.
file1 line1 file1 line2 file2 line1 file2 line2
We will also check the inode number of
stat command before we proceed.
$ stat file2 File: ‘file2’ Size: 24 Blocks: 8 IO Block: 4096 regular file Device: 801h/2049d Inode: 6290327 Links: 1 Access: (0600/-rw-------) Uid: (101642/user) Gid: ( 107/group) Access: 2017-06-09 13:34:45.149894132 +0100 Modify: 2017-06-09 13:34:39.397811729 +0100 Change: 2017-06-09 13:34:39.397811729 +0100 Birth: -
It’s now time to say hello to mr.
$ ed -s file2 <<< $'0r file1\nw'
file2 was modified.
$ cat file2 file1 line1 file1 line2 file2 line1 file2 line2
… and check the inode number.
$ stat file2 File: ‘file2’ Size: 48 Blocks: 8 IO Block: 4096 regular file Device: 801h/2049d Inode: 6290327 Links: 1 Access: (0600/-rw-------) Uid: (101642/user) Gid: ( 107/group) Access: 2017-06-09 13:35:13.902306029 +0100 Modify: 2017-06-09 13:35:11.794275829 +0100 Change: 2017-06-09 13:35:11.794275829 +0100 Birth: -
It has remained as
6290327 meaning this time we actually edited the file
ed can be used interactively however we’re using
<<< here to send
our command string into
<<< is called a Here String and as mentioned it sends a given string to
the stdin of a given command.
We could have also used a pipeline approach e.g.
echo $'0r file1\nw' | ed -s file2
-s option tells
ed to be silent and not produce any output
which is useful for “scripting” or “automating” file edits.
$'' used here is a special form of shell quoting:
“Words of the form
$'string' are treated specially.
The word expands to string, with backslash-escaped characters replaced as specified
by the ANSI C standard.”
This means that the
\n here produces an “actual” newline character.
$ echo $'0r file1\nw' 0r file1 w
Another common approach for generating such a string is to use
$ printf '0r file1\nw\n' 0r file1 w
There is also of course the option of a Here Document:
$ ed -s file2 <<\. 0r file1 w .
So we are dealing with 2
ed commands here:
r command when given a filename will “Read file to
after the addressed line.” We’ve used
0 as the line number
to address as we want to insert the contents before line
as opposed to after.
w command writes the file.