Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
657 views
in Technique[技术] by (71.8m points)

bash - Using Awk or Sed to tack on a statement at the end of a specific line

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, which currently reads:

_cell_length_a

I would like to tack the contents of my variable a (defined in my function as a=5.3827) so that way the line now reads:

_cell_length_a 5.3827

Is there a way to do this using sed or awk? I am using a bash script to accomplish this (the full script is too large to post, unfortunately).

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.
    • Note that the $ 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.
    • Note that since the opening delimiter (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.
    • For debugging, use ,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:

  • destroy symlinks(!) - if the input file was a symlink, it is replaced with a regular file of the same name
    • A common scenario where that matters: say your shell initialization file ~/.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.
    • What's more, BSD sed's behavior even introduces a security risk (see below).
  • do not preserve the original file-creation date (where supported; e.g., on OSX)
  • they do, however,

    • preserve extended attributes (where supported; e.g., on OSX)
    • preserve file permissions

      • Caution: BSD sed introduces a security risk with respect to symlinks (behavior still present as of the version that comes with FreeBSD 10):
        • The symlink's permissions are copied to the replacement file, not the symlink target's. Since symlinks get executable permissions by default, you'll invariably end up with an executable file, whether the input file was executable or not.
      • Fortunately, GNU 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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...