I have a file I named poscar1.cif
, and I would like to insert the contents of a variable at a specific line in this file.
For example, line 24
You can use sed to do it, depending on your answer to dawg's question as
sed -i -e '24s/$/5.3827/' poscar1.cif
or if it's the pattern
sed -i -e '/_cell_length_a/s/$/5.3827/' poscar1.cif
The first goes to the line with the given number, the later will apply on any line that matches the pattern in the first set of slashes. In either case it will then "replace" the end of the line with the value between the final two slashes.
Since the veteran ed utility doesn't get enough attention anymore:
a=5.3827
ed -s poscar1.cif <<EOF
g/^_cell_length_a\$/ s//& $a/
w
EOF
ed
truly edits a file in place, unlike sed
with its -i
option[1].
sed
borrowed many features from ed
, so there is significant overlap in functionality, but there are also important differences, some of which surface here.
-s
suppresses ed
's status messages.poscar1.cif
is the input file to edit in place.<<EOF ...
is the here-document that contains the commands for ed
- ed
requires its commands to come from stdin and each command to be on its own line.g/^_cell_length_a\$/
... is a (basic) regex (regular expression) that matches all lines that exactly contain _cell_length_a
- the g
ensures that no error is reported if there's no match at all.
$
is \
-escaped to protect it from interpretation by the shell inside the here-document (not strictly necessary in this instance, but good practice).s//& $a/
... //
repeats the search for the most recently used regex on a matching line and replaces the match with itself (&
), followed by a space and the value of variable $a
.
EOF
) of the here-document is unquoted, shell variable expansions DO take place; in essence, the contents are treated by the shell like the contents of a double-quoted string.w
writes the modified buffer back to the input file.
,p
in place of w
so as to only print the modified buffer, without writing it back to the file.[1] Re in-place updating:
More precisely, ed
preserves the file's existing inode, which ensures that all the file's attributes are preserved.
However, it does not overwrite individual bytes of the existing file, but reads the entire file into a buffer in memory, and writes the entire buffer to the file when asked to.
This makes ed
suitable only for files small enough to be read into memory as a whole.
By contrast, sed -i
(GNU and BSD sed
), its GNU 4.1+ counterpart, awk -i inplace
, and also perl -i
replace the original file with a newly created one, which implies that they:
~/.bashrc
is a symlink to a file elsewhere you keep under source control; you then install a tool that uses sed -i
to modify ~/.bashrc
, which results in it being replaced with a regular file, and the link to your source-controlled version is broken.sed
's behavior even introduces a security risk (see below).they do, however,
preserve file permissions
sed
introduces a security risk with respect to symlinks (behavior still present as of the version that comes with FreeBSD 10):
sed
handles this scenario properly.sed
, gawk
, and perl
could address the issues above by taking extra steps, but there's one thing that can only be ensured if the original inode is retained, as ed
does:
When a file is being monitored for changes by its inode number (e.g., with tail -f
), not preserving the inode breaks that monitoring.
Using your example, you could do something like this:
sed -i 's/\(_cell_length_a\)/\1 5.3827/' poscar1.cif
where,
-i
option says to edit the file in place, rather than creating a copyposcar1.cif
is the fileThe regex syntax is hard to read. The basic format to find and replace is:
s/find/replace/
Where find
is the text of the line you're looking for and replace
is the text to replace that text with.
If we want to use part of the find string in our replacement, we group it by surrounding it with \(
and \)
and then use \1
to refer to it in the replacement string. The following appends replace to any line consisting of find:
s/\(find\)/\1replace/
Keep in mind that there are special escape characters or meta characters that you have to treat specially if your string contains them.