I'm rearranging the order of the quoted sections. On Sun, Jan 02, 2022 at 08:14:47PM -0500, The Wanderer wrote: > That's very interesting, although not all that accessible to the > relative newcomer to the field. It does leave me sad about the apparent > conclusion that there is no safe way to edit a file programmatically > (i.e., not via an interactive editor, and possibly not at all) in a way > which preserves the inode; the reasons why I thought of that as > desirable seem less clearly so on further analysis, but it's still > unfortunate for a thing to not be possible to do.
There are several things going on here. I'll address two of them. First: in order to "preserve the inode", you would need to open the file in either read+write mode, or clobber+write mode, and then start overwriting the file once you have the content in memory. This is inherently dangerous, because if the writing is interrupted before completion, the file is now corrupted (in the read+write case), or truncated (in the clobber+write case). The writing can be interrupted if the process is terminated, or if the system crashes. Even with uninterruptible power supplies, there's no 100% sure way to prevent a system crash, so there is always a risk. Second: the idea that a program should be altering (overwriting) the contents of configuration files *at all* is sketchy. It's a really bad design. The types of files that people usually want to alter with sed are designed to be edited by humans. They are *not* amenable to programmatic alteration, which is why the answers that people come up with are so contorted. If you really need to alter a textual configuration file on a bunch of servers, I suggest editing the file by hand on one machine, making a diff out of that, and then using patch to apply that diff on all the other machines. Or use ansible, chef, puppet, etc. This is not a new or unique problem. Stop reinventing wheels. A much better approach is what people have started doing with ".d" directories. Instead of a single monolithic text configuration file that requires a human brain to comprehend, you put individual pieces of configuration in separate files. If you want to change the phlogiston level, you just put the new value in the foobar.d/phlogiston file. No need for a parser to find the phlogiston variable in a text file. Of course, the program being configured has to be written to support such a thing. > > unicorn:~$ printf %s\\n foo foo foo | awk '!done && /foo/{$0="bar"; done=1} > > 1' > > bar > > foo > > foo > > I keep forgetting that printf is even a thing in shell, and while awk > did occur to my mind, I don't know it well enough to do anything useful > in it. I can only kind-of parse the syntax you gave there, and that only > because I just saw very similar-looking syntax given for sed in the > Stack Overflow answer linked above. An awk program consists of a series of alternating conditions and actions. Each line of input is matched against the conditions, in order. If any of the conditions matches the input line, then the corresponding action block is executed. In my example, there are two condition/action pairs: 1) Condition: !done && /foo/ Action: {$0="bar"; done=1} 2) Condition: 1 (always true) Action: omitted, so the default action of "print" is used. The second pair could be written out more explicitly, but I used the extremely condensed form. So, what this does is look for lines containing the regex "foo", which is equivalent to simply matching the substring "foo". If such a line is found, and if the "done" variable is NOT set to a nonzero value, then we change the whole input line to "bar", and then set the done variable to 1. This prevents the condition from triggering a second time. The second condition matches every line, so that we always print something. Either we print "bar" (if the first action was triggered), or else we print a copy of the original input line. All together, what this does is "the first time we match foo, print bar instead; otherwise copy the input line", which is what was requested.